Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c10888e6fe | ||
|
|
93455f8eb5 | ||
|
|
65acd96c8d | ||
|
|
fabe720b9b | ||
|
|
53dd313e97 | ||
|
|
2678d7a660 | ||
|
|
46718102c6 | ||
|
|
0f473fe3b1 | ||
|
|
2872602c9e | ||
|
|
ab653cae33 | ||
|
|
e6a035e575 | ||
|
|
fbe48d75dd | ||
|
|
20ba0f49dd | ||
|
|
c213e9878d | ||
|
|
9704ca4c18 | ||
|
|
49dce6219e | ||
|
|
260ff6c94f | ||
|
|
3ec281d104 |
@@ -10,6 +10,6 @@ task:
|
||||
- pkg upgrade -y
|
||||
- pkg install -y pkgconf vips node npm
|
||||
install_script:
|
||||
- npm install --unsafe-perm
|
||||
- npm install --build-from-source --unsafe-perm
|
||||
test_script:
|
||||
- npm test
|
||||
|
||||
70
.travis.yml
@@ -8,7 +8,7 @@ jobs:
|
||||
- sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
|
||||
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_10.x | bash -"
|
||||
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
|
||||
install: sudo docker exec sharp bash -c "npm install --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"
|
||||
|
||||
- name: "Linux x64 (CentOS 7, glibc 2.17) - Node.js 12"
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
|
||||
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_12.x | bash -"
|
||||
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
|
||||
install: sudo docker exec sharp bash -c "npm install --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"
|
||||
|
||||
- name: "Linux x64 (CentOS 7, glibc 2.17) - Node.js 14"
|
||||
@@ -30,7 +30,19 @@ jobs:
|
||||
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
|
||||
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_14.x | bash -"
|
||||
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
|
||||
install: sudo docker exec sharp bash -c "npm install --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"
|
||||
|
||||
- name: "Linux x64 (CentOS 7, glibc 2.17) - 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 centos:7
|
||||
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_15.x | bash -"
|
||||
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
|
||||
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"
|
||||
@@ -40,7 +52,7 @@ jobs:
|
||||
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 exec sharp apk add build-base git python2 --update-cache
|
||||
install: sudo docker exec sharp sh -c "npm install --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"
|
||||
|
||||
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 12"
|
||||
@@ -50,7 +62,7 @@ jobs:
|
||||
before_install:
|
||||
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12.0-alpine
|
||||
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
||||
install: sudo docker exec sharp sh -c "npm install --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"
|
||||
|
||||
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 14"
|
||||
@@ -60,7 +72,18 @@ jobs:
|
||||
before_install:
|
||||
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14.0-alpine
|
||||
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
||||
install: sudo docker exec sharp sh -c "npm install --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"
|
||||
|
||||
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - 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 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 ARM64v8 (Debian 11, glibc 2.29) - Node.js 10"
|
||||
@@ -74,7 +97,7 @@ jobs:
|
||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_10.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs=10.*"
|
||||
install: sudo docker exec sharp sh -c "npm install --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"
|
||||
|
||||
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 12"
|
||||
@@ -88,7 +111,7 @@ jobs:
|
||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_12.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||
install: sudo docker exec sharp sh -c "npm install --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"
|
||||
|
||||
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 14"
|
||||
@@ -102,7 +125,22 @@ jobs:
|
||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_14.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||
install: sudo docker exec sharp sh -c "npm install --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"
|
||||
|
||||
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 15"
|
||||
arch: arm64
|
||||
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 arm64v8/debian:bullseye
|
||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python2 curl"
|
||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_15.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||
script: sudo docker exec sharp sh -c "npm test"
|
||||
|
||||
- name: "macOS (10.13) - Node.js 10"
|
||||
@@ -110,6 +148,7 @@ jobs:
|
||||
osx_image: xcode10.1
|
||||
language: node_js
|
||||
node_js: "10"
|
||||
install: npm install --build-from-source
|
||||
|
||||
- name: "macOS (10.13) - Node.js 12"
|
||||
os: osx
|
||||
@@ -117,6 +156,7 @@ jobs:
|
||||
language: node_js
|
||||
node_js: "12"
|
||||
before_install: unset prebuild_upload
|
||||
install: npm install --build-from-source
|
||||
|
||||
- name: "macOS (10.13) - Node.js 14"
|
||||
os: osx
|
||||
@@ -124,13 +164,23 @@ jobs:
|
||||
language: node_js
|
||||
node_js: "14"
|
||||
before_install: unset prebuild_upload
|
||||
install: npm install --build-from-source
|
||||
|
||||
- name: "macOS (10.13) - Node.js 15"
|
||||
os: osx
|
||||
osx_image: xcode10.1
|
||||
language: node_js
|
||||
node_js: "15"
|
||||
before_install: unset prebuild_upload
|
||||
install: npm install --build-from-source
|
||||
|
||||
- name: "Unit test coverage report"
|
||||
os: linux
|
||||
dist: bionic
|
||||
language: node_js
|
||||
node_js: "13"
|
||||
node_js: "14"
|
||||
before_install: unset prebuild_upload
|
||||
install: npm install --build-from-source
|
||||
after_success:
|
||||
- npm install coveralls
|
||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||
|
||||
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
|
||||
As well as image resizing, operations such as
|
||||
rotation, extraction, compositing and gamma correction are available.
|
||||
|
||||
Most modern macOS, Windows and Linux systems running Node.js v10.16.0+
|
||||
Most modern macOS, Windows and Linux systems running Node.js v10+
|
||||
do not require any additional install or runtime dependencies.
|
||||
|
||||
## Examples
|
||||
|
||||
10
appveyor.yml
@@ -1,4 +1,4 @@
|
||||
os: Visual Studio 2017
|
||||
os: Visual Studio 2019
|
||||
version: "{build}"
|
||||
build: off
|
||||
environment:
|
||||
@@ -19,8 +19,14 @@ environment:
|
||||
- nodejs_version: "14"
|
||||
platform: x64
|
||||
prebuild_upload: ""
|
||||
- nodejs_version: "15"
|
||||
platform: x86
|
||||
prebuild_upload: ""
|
||||
- nodejs_version: "15"
|
||||
platform: x64
|
||||
prebuild_upload: ""
|
||||
install:
|
||||
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) $env:platform
|
||||
- npm install
|
||||
- npm install --build-from-source
|
||||
test_script:
|
||||
- npm test
|
||||
|
||||
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
|
||||
As well as image resizing, operations such as
|
||||
rotation, extraction, compositing and gamma correction are available.
|
||||
|
||||
Most modern macOS, Windows and Linux systems running Node.js v10.16.0+
|
||||
Most modern macOS, Windows and Linux systems running Node.js v10+
|
||||
do not require any additional install or runtime dependencies.
|
||||
|
||||
### Formats
|
||||
|
||||
@@ -65,6 +65,61 @@ The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## affine
|
||||
|
||||
Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
|
||||
|
||||
You must provide an array of length 4 or a 2x2 affine transformation matrix.
|
||||
By default, new pixels are filled with a black background. You can provide a background color with the `background` option.
|
||||
A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.nohalo`.
|
||||
|
||||
In the case of a 2x2 matrix, the transform is:
|
||||
|
||||
- X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
|
||||
- Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
|
||||
|
||||
where:
|
||||
|
||||
- x and y are the coordinates in input image.
|
||||
- X and Y are the coordinates in output image.
|
||||
- (0,0) is the upper left corner.
|
||||
|
||||
### Parameters
|
||||
|
||||
- `matrix` **([Array][7]<[Array][7]<[number][1]>> | [Array][7]<[number][1]>)** affine transformation matrix
|
||||
- `options` **[Object][2]?** if present, is an Object with optional attributes.
|
||||
- `options.background` **([String][3] \| [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
|
||||
- `options.idx` **[Number][1]** input horizontal offset (optional, default `0`)
|
||||
- `options.idy` **[Number][1]** input vertical offset (optional, default `0`)
|
||||
- `options.odx` **[Number][1]** output horizontal offset (optional, default `0`)
|
||||
- `options.ody` **[Number][1]** output vertical offset (optional, default `0`)
|
||||
- `options.interpolator` **[String][3]** interpolator (optional, default `sharp.interpolators.bicubic`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const pipeline = sharp()
|
||||
.affine([[1, 0.3], [0.1, 0.7]], {
|
||||
background: 'white',
|
||||
interpolate: sharp.interpolators.nohalo
|
||||
})
|
||||
.toBuffer((err, outputBuffer, info) => {
|
||||
// outputBuffer contains the transformed image
|
||||
// info.width and info.height contain the new dimensions
|
||||
});
|
||||
|
||||
inputStream
|
||||
.pipe(pipeline);
|
||||
```
|
||||
|
||||
- Throws **[Error][5]** Invalid parameters
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
**Meta**
|
||||
|
||||
- **since**: 0.27.0
|
||||
|
||||
## sharpen
|
||||
|
||||
Sharpen the image.
|
||||
|
||||
@@ -372,6 +372,8 @@ Warning: multiple sharp instances concurrently producing tile output can expose
|
||||
- `options.skipBlanks` **[number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
|
||||
- `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
||||
- `options.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`. (optional, default `'dz'`)
|
||||
- `options.centre` **[boolean][7]** centre image in tile. (optional, default `false`)
|
||||
- `options.center` **[boolean][7]** alternative spelling of centre. (optional, default `false`)
|
||||
|
||||
### Examples
|
||||
|
||||
|
||||
@@ -209,7 +209,8 @@ Returns **Sharp**
|
||||
Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
|
||||
Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
|
||||
|
||||
The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
||||
The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
|
||||
will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
@@ -12,6 +12,36 @@ console.log(sharp.format);
|
||||
|
||||
Returns **[Object][1]**
|
||||
|
||||
## interpolators
|
||||
|
||||
An Object containing the available interpolators and their proper values
|
||||
|
||||
Type: [string][2]
|
||||
|
||||
### nearest
|
||||
|
||||
[Nearest neighbour interpolation][3]. Suitable for image enlargement only.
|
||||
|
||||
### bilinear
|
||||
|
||||
[Bilinear interpolation][4]. Faster than bicubic but with less smooth results.
|
||||
|
||||
### bicubic
|
||||
|
||||
[Bicubic interpolation][5] (the default).
|
||||
|
||||
### locallyBoundedBicubic
|
||||
|
||||
[LBB interpolation][6]. Prevents some "[acutance][7]" but typically reduces performance by a factor of 2.
|
||||
|
||||
### nohalo
|
||||
|
||||
[Nohalo interpolation][8]. Prevents acutance but typically reduces performance by a factor of 3.
|
||||
|
||||
### vertexSplitQuadraticBasisSpline
|
||||
|
||||
[VSQBS interpolation][9]. Prevents "staircasing" when enlarging.
|
||||
|
||||
## versions
|
||||
|
||||
An Object containing the version numbers of libvips and its dependencies.
|
||||
@@ -31,10 +61,10 @@ useful for determining how much working memory is required for a particular task
|
||||
|
||||
### Parameters
|
||||
|
||||
- `options` **([Object][1] \| [boolean][2])** Object with the following attributes, or boolean where true uses default cache settings and false removes all caching (optional, default `true`)
|
||||
- `options.memory` **[number][3]** is the maximum memory in MB to use for this cache (optional, default `50`)
|
||||
- `options.files` **[number][3]** is the maximum number of files to hold open (optional, default `20`)
|
||||
- `options.items` **[number][3]** is the maximum number of operations to cache (optional, default `100`)
|
||||
- `options` **([Object][1] \| [boolean][10])** Object with the following attributes, or boolean where true uses default cache settings and false removes all caching (optional, default `true`)
|
||||
- `options.memory` **[number][11]** is the maximum memory in MB to use for this cache (optional, default `50`)
|
||||
- `options.files` **[number][11]** is the maximum number of files to hold open (optional, default `20`)
|
||||
- `options.items` **[number][11]** is the maximum number of operations to cache (optional, default `100`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -64,7 +94,7 @@ This method always returns the current concurrency.
|
||||
|
||||
### Parameters
|
||||
|
||||
- `concurrency` **[number][3]?**
|
||||
- `concurrency` **[number][11]?**
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -74,7 +104,7 @@ sharp.concurrency(2); // 2
|
||||
sharp.concurrency(0); // 4
|
||||
```
|
||||
|
||||
Returns **[number][3]** concurrency
|
||||
Returns **[number][11]** concurrency
|
||||
|
||||
## queue
|
||||
|
||||
@@ -116,7 +146,7 @@ by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM N
|
||||
|
||||
### Parameters
|
||||
|
||||
- `simd` **[boolean][2]** (optional, default `true`)
|
||||
- `simd` **[boolean][10]** (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -130,10 +160,26 @@ const simd = sharp.simd(false);
|
||||
// prevent libvips from using liborc at runtime
|
||||
```
|
||||
|
||||
Returns **[boolean][2]**
|
||||
Returns **[boolean][10]**
|
||||
|
||||
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||
|
||||
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||
|
||||
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
[3]: http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation
|
||||
|
||||
[4]: http://en.wikipedia.org/wiki/Bilinear_interpolation
|
||||
|
||||
[5]: http://en.wikipedia.org/wiki/Bicubic_interpolation
|
||||
|
||||
[6]: https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100
|
||||
|
||||
[7]: http://en.wikipedia.org/wiki/Acutance
|
||||
|
||||
[8]: http://eprints.soton.ac.uk/268086/
|
||||
|
||||
[9]: https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48
|
||||
|
||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
|
||||
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
|
||||
@@ -4,6 +4,29 @@
|
||||
|
||||
Requires libvips v8.10.0
|
||||
|
||||
### v0.26.3 - 16th November 2020
|
||||
|
||||
* Expose libvips' affine operation.
|
||||
[#2336](https://github.com/lovell/sharp/pull/2336)
|
||||
[@guillevc](https://github.com/guillevc)
|
||||
|
||||
* Fallback to tar.gz for prebuilt libvips when Brotli not available.
|
||||
[#2412](https://github.com/lovell/sharp/pull/2412)
|
||||
[@ascorbic](https://github.com/ascorbic)
|
||||
|
||||
### v0.26.2 - 14th October 2020
|
||||
|
||||
* Add support for EXR input. Requires libvips compiled with OpenEXR.
|
||||
[#698](https://github.com/lovell/sharp/issues/698)
|
||||
|
||||
* Ensure support for yarn v2.
|
||||
[#2379](https://github.com/lovell/sharp/pull/2379)
|
||||
[@jalovatt](https://github.com/jalovatt)
|
||||
|
||||
* Add centre/center option to tile-based output.
|
||||
[#2397](https://github.com/lovell/sharp/pull/2397)
|
||||
[@beig](https://github.com/beig)
|
||||
|
||||
### v0.26.1 - 20th September 2020
|
||||
|
||||
* Ensure correct pageHeight when verifying multi-page image dimensions.
|
||||
|
||||
@@ -203,3 +203,6 @@ GitHub: https://github.com/derom
|
||||
|
||||
Name: Stefan Probst
|
||||
GitHub: https://github.com/stefanprobst
|
||||
|
||||
Name: Thomas Beiganz
|
||||
GitHub: https://github.com/beig
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
|
||||
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
||||
<link rel="author" href="/humans.txt" type="text/plain">
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.26.1/docs/README.md" as="fetch" type="text/markdown" crossorigin>
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.26.3/docs/README.md" as="fetch" type="text/markdown" crossorigin>
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://pixel.plumbing">
|
||||
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
||||
@@ -139,7 +139,7 @@
|
||||
docuteApiTitlePlugin,
|
||||
docuteApiSearchPlugin
|
||||
],
|
||||
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.26.1/docs',
|
||||
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.26.3/docs',
|
||||
nav: [
|
||||
{
|
||||
title: 'Funding',
|
||||
|
||||
@@ -10,19 +10,19 @@ yarn add sharp
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Node.js v10.16.0+
|
||||
* Node.js v10+
|
||||
|
||||
## Prebuilt binaries
|
||||
|
||||
Ready-compiled sharp and libvips binaries are provided for use with
|
||||
Node.js v10.16.0+ on the most common platforms:
|
||||
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
|
||||
|
||||
A ~7MB tarball containing libvips and its most commonly used dependencies
|
||||
A 7-8MB 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
|
||||
@@ -89,11 +89,14 @@ To install the prebuilt libvips binaries from a custom URL,
|
||||
set the `sharp_libvips_binary_host` npm config option
|
||||
or the `npm_config_sharp_libvips_binary_host` environment variable.
|
||||
|
||||
The version subpath and file name are appended to these.
|
||||
The version subpath and file name are appended to these. There should be tarballs available
|
||||
that are compressed with both gzip and Brotli, as the format downloaded will vary depending
|
||||
on whether the user's version of Node supports Brotli decompression (Node.js v10.16.0+)
|
||||
|
||||
For example, if `sharp_libvips_binary_host` is set to `https://hostname/path`
|
||||
and the libvips version is `1.2.3` then the resultant URL will be
|
||||
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.br`.
|
||||
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.br` or
|
||||
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.gz`.
|
||||
|
||||
See the Chinese mirror below for a further example.
|
||||
|
||||
@@ -147,10 +150,18 @@ The binaries in the `node_modules` directory of the
|
||||
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html)
|
||||
must be for the Linux x64 platform.
|
||||
|
||||
On machines other than Linux x64, such as macOS and Windows, run the following:
|
||||
When building your deployment package on machines other than Linux x64 (glibc),
|
||||
run the following commands:
|
||||
|
||||
macOS:
|
||||
```sh
|
||||
rm -rf node_modules/sharp
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux sharp
|
||||
```
|
||||
|
||||
Windows:
|
||||
```sh
|
||||
rmdir /s /q node_modules/sharp
|
||||
npm install --arch=x64 --platform=linux sharp
|
||||
```
|
||||
|
||||
@@ -164,20 +175,6 @@ docker run -v "$PWD":/var/task lambci/lambda:build-nodejs12.x npm install sharp
|
||||
To get the best performance select the largest memory available.
|
||||
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
|
||||
|
||||
## Electron
|
||||
|
||||
Electron provides versions of the V8 JavaScript engine
|
||||
that are incompatible with Node.js.
|
||||
To ensure the correct binaries are used, run the following:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npx electron-rebuild
|
||||
```
|
||||
|
||||
Further help can be found at
|
||||
[https://electronjs.org/docs/tutorial/using-native-node-modules](https://electronjs.org/docs/tutorial/using-native-node-modules)
|
||||
|
||||
## Worker threads
|
||||
|
||||
The main thread must call `require('sharp')`
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { extractDescription, extractKeywords } = require('./extract');
|
||||
|
||||
const searchIndex = [];
|
||||
|
||||
// Install
|
||||
const contents = fs.readFileSync(`${__dirname}/../install.md`, 'utf8');
|
||||
const contents = fs.readFileSync(path.join(__dirname, '..', 'install.md'), 'utf8');
|
||||
const matches = contents.matchAll(
|
||||
/## (?<title>[A-Za-z ]+)\n\n(?<body>[^#]+)/gs
|
||||
);
|
||||
@@ -34,7 +35,7 @@ for (const match of matches) {
|
||||
'colour',
|
||||
'utility'
|
||||
].forEach((section) => {
|
||||
const contents = fs.readFileSync(`${__dirname}/../api-${section}.md`, 'utf8');
|
||||
const contents = fs.readFileSync(path.join(__dirname, '..', `api-${section}.md`), 'utf8');
|
||||
const matches = contents.matchAll(
|
||||
/\n## (?<title>[A-Za-z]+)\n\n(?<firstparagraph>.+?)\n\n/gs
|
||||
);
|
||||
@@ -54,6 +55,6 @@ for (const match of matches) {
|
||||
});
|
||||
|
||||
fs.writeFileSync(
|
||||
`${__dirname}/../search-index.json`,
|
||||
path.join(__dirname, '..', 'search-index.json'),
|
||||
JSON.stringify(searchIndex)
|
||||
);
|
||||
|
||||
@@ -1,65 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const stopWords = [
|
||||
'a',
|
||||
'about',
|
||||
'all',
|
||||
'already',
|
||||
'always',
|
||||
'an',
|
||||
'and',
|
||||
'any',
|
||||
'are',
|
||||
'as',
|
||||
'at',
|
||||
'be',
|
||||
'been',
|
||||
'by',
|
||||
'can',
|
||||
'do',
|
||||
'does',
|
||||
'each',
|
||||
'either',
|
||||
'etc',
|
||||
'for',
|
||||
'from',
|
||||
'get',
|
||||
'gets',
|
||||
'has',
|
||||
'have',
|
||||
'how',
|
||||
'if',
|
||||
'in',
|
||||
'is',
|
||||
'it',
|
||||
'its',
|
||||
'may',
|
||||
'more',
|
||||
'much',
|
||||
'no',
|
||||
'not',
|
||||
'of',
|
||||
'on',
|
||||
'or',
|
||||
'over',
|
||||
'set',
|
||||
'sets',
|
||||
'should',
|
||||
'that',
|
||||
'the',
|
||||
'their',
|
||||
'there',
|
||||
'therefore',
|
||||
'these',
|
||||
'this',
|
||||
'to',
|
||||
'use',
|
||||
'using',
|
||||
'when',
|
||||
'which',
|
||||
'will',
|
||||
'with'
|
||||
];
|
||||
const stopWords = require('./stop-words');
|
||||
|
||||
const extractDescription = (str) =>
|
||||
str
|
||||
@@ -71,10 +12,13 @@ const extractDescription = (str) =>
|
||||
.trim();
|
||||
|
||||
const extractKeywords = (str) =>
|
||||
str
|
||||
.split(/[ -/]/)
|
||||
.map((word) => word.toLowerCase().replace(/[^a-z]/g, ''))
|
||||
.filter((word) => word.length > 2 && !stopWords.includes(word))
|
||||
.join(' ');
|
||||
[
|
||||
...new Set(
|
||||
str
|
||||
.split(/[ -/]/)
|
||||
.map((word) => word.toLowerCase().replace(/[^a-z]/g, ''))
|
||||
.filter((word) => word.length > 2 && !stopWords.includes(word))
|
||||
)
|
||||
].join(' ');
|
||||
|
||||
module.exports = { extractDescription, extractKeywords };
|
||||
|
||||
71
docs/search-index/stop-words.js
Normal file
@@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
'about',
|
||||
'after',
|
||||
'all',
|
||||
'already',
|
||||
'alternative',
|
||||
'always',
|
||||
'and',
|
||||
'any',
|
||||
'are',
|
||||
'been',
|
||||
'before',
|
||||
'can',
|
||||
'containing',
|
||||
'default',
|
||||
'does',
|
||||
'each',
|
||||
'either',
|
||||
'etc',
|
||||
'for',
|
||||
'from',
|
||||
'get',
|
||||
'gets',
|
||||
'given',
|
||||
'has',
|
||||
'have',
|
||||
'how',
|
||||
'image',
|
||||
'its',
|
||||
'may',
|
||||
'more',
|
||||
'most',
|
||||
'much',
|
||||
'must',
|
||||
'non',
|
||||
'not',
|
||||
'occur',
|
||||
'occurs',
|
||||
'over',
|
||||
'perform',
|
||||
'performs',
|
||||
'provide',
|
||||
'provided',
|
||||
'set',
|
||||
'sets',
|
||||
'should',
|
||||
'spelling',
|
||||
'support',
|
||||
'supported',
|
||||
'take',
|
||||
'that',
|
||||
'the',
|
||||
'their',
|
||||
'there',
|
||||
'therefore',
|
||||
'these',
|
||||
'this',
|
||||
'use',
|
||||
'used',
|
||||
'using',
|
||||
'value',
|
||||
'values',
|
||||
'when',
|
||||
'which',
|
||||
'while',
|
||||
'will',
|
||||
'with',
|
||||
'without'
|
||||
];
|
||||
@@ -25,6 +25,7 @@ const minimumGlibcVersionByArch = {
|
||||
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
||||
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
||||
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
||||
const supportsBrotli = ('BrotliDecompress' in zlib);
|
||||
|
||||
const fail = function (err) {
|
||||
npmLog.error('sharp', err.message);
|
||||
@@ -43,7 +44,7 @@ const extractTarball = function (tarPath) {
|
||||
libvips.mkdirSync(versionedVendorPath);
|
||||
stream.pipeline(
|
||||
fs.createReadStream(tarPath),
|
||||
new zlib.BrotliDecompress(),
|
||||
supportsBrotli ? new zlib.BrotliDecompress() : new zlib.Gunzip(),
|
||||
tarFs.extract(versionedVendorPath),
|
||||
function (err) {
|
||||
if (err) {
|
||||
@@ -58,6 +59,7 @@ const extractTarball = function (tarPath) {
|
||||
|
||||
try {
|
||||
const useGlobalLibvips = libvips.useGlobalLibvips();
|
||||
|
||||
if (useGlobalLibvips) {
|
||||
const globalLibvipsVersion = libvips.globalLibvipsVersion();
|
||||
npmLog.info('sharp', `Detected globally-installed libvips v${globalLibvipsVersion}`);
|
||||
@@ -80,11 +82,16 @@ try {
|
||||
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
||||
}
|
||||
}
|
||||
if (!semver.satisfies(process.versions.node, process.env.npm_package_engines_node)) {
|
||||
throw new Error(`Expected Node.js version ${process.env.npm_package_engines_node} but found ${process.versions.node}`);
|
||||
|
||||
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
|
||||
if (!semver.satisfies(process.versions.node, supportedNodeVersion)) {
|
||||
throw new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`);
|
||||
}
|
||||
|
||||
const extension = supportsBrotli ? 'br' : 'gz';
|
||||
|
||||
// Download to per-process temporary file
|
||||
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.br';
|
||||
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.' + extension;
|
||||
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
||||
if (fs.existsSync(tarPathCache)) {
|
||||
npmLog.info('sharp', `Using cached ${tarPathCache}`);
|
||||
|
||||
@@ -155,6 +155,13 @@ const Sharp = function (input, options) {
|
||||
extendRight: 0,
|
||||
extendBackground: [0, 0, 0, 255],
|
||||
withoutEnlargement: false,
|
||||
affineMatrix: [],
|
||||
affineBackground: [0, 0, 0, 255],
|
||||
affineIdx: 0,
|
||||
affineIdy: 0,
|
||||
affineOdx: 0,
|
||||
affineOdy: 0,
|
||||
affineInterpolator: this.constructor.interpolators.bilinear,
|
||||
kernel: 'lanczos3',
|
||||
fastShrinkOnLoad: true,
|
||||
// operations
|
||||
@@ -238,6 +245,7 @@ const Sharp = function (input, options) {
|
||||
tileAngle: 0,
|
||||
tileSkipBlanks: -1,
|
||||
tileBackground: [255, 255, 255, 255],
|
||||
tileCentre: false,
|
||||
linearA: 1,
|
||||
linearB: 0,
|
||||
// Function to notify of libvips warnings
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { flatten: flattenArray } = require('array-flatten');
|
||||
const color = require('color');
|
||||
const is = require('./is');
|
||||
|
||||
@@ -82,6 +83,103 @@ function flop (flop) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
|
||||
*
|
||||
* You must provide an array of length 4 or a 2x2 affine transformation matrix.
|
||||
* By default, new pixels are filled with a black background. You can provide a background color with the `background` option.
|
||||
* A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.nohalo`.
|
||||
*
|
||||
* In the case of a 2x2 matrix, the transform is:
|
||||
* - X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
|
||||
* - Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
|
||||
*
|
||||
* where:
|
||||
* - x and y are the coordinates in input image.
|
||||
* - X and Y are the coordinates in output image.
|
||||
* - (0,0) is the upper left corner.
|
||||
*
|
||||
* @since 0.27.0
|
||||
*
|
||||
* @example
|
||||
* const pipeline = sharp()
|
||||
* .affine([[1, 0.3], [0.1, 0.7]], {
|
||||
* background: 'white',
|
||||
* interpolate: sharp.interpolators.nohalo
|
||||
* })
|
||||
* .toBuffer((err, outputBuffer, info) => {
|
||||
* // outputBuffer contains the transformed image
|
||||
* // info.width and info.height contain the new dimensions
|
||||
* });
|
||||
*
|
||||
* inputStream
|
||||
* .pipe(pipeline);
|
||||
*
|
||||
* @param {Array<Array<number>>|Array<number>} matrix - affine transformation matrix
|
||||
* @param {Object} [options] - if present, is an Object with optional attributes.
|
||||
* @param {String|Object} [options.background="#000000"] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
* @param {Number} [options.idx=0] - input horizontal offset
|
||||
* @param {Number} [options.idy=0] - input vertical offset
|
||||
* @param {Number} [options.odx=0] - output horizontal offset
|
||||
* @param {Number} [options.ody=0] - output vertical offset
|
||||
* @param {String} [options.interpolator=sharp.interpolators.bicubic] - interpolator
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function affine (matrix, options) {
|
||||
const flatMatrix = flattenArray(matrix);
|
||||
if (flatMatrix.length === 4 && flatMatrix.every(is.number)) {
|
||||
this.options.affineMatrix = flatMatrix;
|
||||
} else {
|
||||
throw is.invalidParameterError('matrix', '1x4 or 2x2 array', matrix);
|
||||
}
|
||||
|
||||
if (is.defined(options)) {
|
||||
if (is.object(options)) {
|
||||
this._setBackgroundColourOption('affineBackground', options.background);
|
||||
if (is.defined(options.idx)) {
|
||||
if (is.number(options.idx)) {
|
||||
this.options.affineIdx = options.idx;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.idx', 'number', options.idx);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.idy)) {
|
||||
if (is.number(options.idy)) {
|
||||
this.options.affineIdy = options.idy;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.idy', 'number', options.idy);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.odx)) {
|
||||
if (is.number(options.odx)) {
|
||||
this.options.affineOdx = options.odx;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.odx', 'number', options.odx);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.ody)) {
|
||||
if (is.number(options.ody)) {
|
||||
this.options.affineOdy = options.ody;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.ody', 'number', options.ody);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.interpolator)) {
|
||||
if (is.inArray(options.interpolator, Object.values(this.constructor.interpolators))) {
|
||||
this.options.affineInterpolator = options.interpolator;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.interpolator', 'valid interpolator name', options.interpolator);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw is.invalidParameterError('options', 'object', options);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sharpen the image.
|
||||
* When used without parameters, performs a fast, mild sharpen of the output image.
|
||||
@@ -482,6 +580,7 @@ module.exports = function (Sharp) {
|
||||
rotate,
|
||||
flip,
|
||||
flop,
|
||||
affine,
|
||||
sharpen,
|
||||
median,
|
||||
blur,
|
||||
|
||||
@@ -658,6 +658,8 @@ function raw () {
|
||||
* @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
|
||||
* @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
|
||||
* @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`.
|
||||
* @param {boolean} [options.centre=false] centre image in tile.
|
||||
* @param {boolean} [options.center=false] alternative spelling of centre.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
@@ -726,6 +728,11 @@ function tile (options) {
|
||||
} else if (is.defined(options.layout) && options.layout === 'google') {
|
||||
this.options.tileSkipBlanks = 5;
|
||||
}
|
||||
// Center image in tile
|
||||
const centre = is.bool(options.center) ? options.center : options.centre;
|
||||
if (is.defined(centre)) {
|
||||
this._setBooleanOption('tileCentre', centre);
|
||||
}
|
||||
}
|
||||
// Format
|
||||
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
||||
|
||||
@@ -386,7 +386,8 @@ function extract (options) {
|
||||
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
|
||||
* Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
|
||||
*
|
||||
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
||||
* The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
|
||||
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
||||
*
|
||||
* @param {number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
|
||||
* @returns {Sharp}
|
||||
|
||||
@@ -13,6 +13,26 @@ const sharp = require('../build/Release/sharp.node');
|
||||
*/
|
||||
const format = sharp.format();
|
||||
|
||||
/**
|
||||
* An Object containing the available interpolators and their proper values
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
const interpolators = {
|
||||
/** [Nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation). Suitable for image enlargement only. */
|
||||
nearest: 'nearest',
|
||||
/** [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation). Faster than bicubic but with less smooth results. */
|
||||
bilinear: 'bilinear',
|
||||
/** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
|
||||
bicubic: 'bicubic',
|
||||
/** [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */
|
||||
locallyBoundedBicubic: 'lbb',
|
||||
/** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
|
||||
nohalo: 'nohalo',
|
||||
/** [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
|
||||
vertexSplitQuadraticBasisSpline: 'vsqbs'
|
||||
};
|
||||
|
||||
/**
|
||||
* An Object containing the version numbers of libvips and its dependencies.
|
||||
* @member
|
||||
@@ -146,6 +166,7 @@ module.exports = function (Sharp) {
|
||||
Sharp[f.name] = f;
|
||||
});
|
||||
Sharp.format = format;
|
||||
Sharp.interpolators = interpolators;
|
||||
Sharp.versions = versions;
|
||||
Sharp.queue = queue;
|
||||
};
|
||||
|
||||
20
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.1",
|
||||
"version": "0.26.3",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -69,7 +69,8 @@
|
||||
"Edward Silverton <e.silverton@gmail.com>",
|
||||
"Roman Malieiev <aromaleev@gmail.com>",
|
||||
"Tomas Szabo <tomas.szabo@deftomat.com>",
|
||||
"Robert O'Rourke <robert@o-rourke.org>"
|
||||
"Robert O'Rourke <robert@o-rourke.org>",
|
||||
"Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
||||
@@ -112,30 +113,31 @@
|
||||
"vips"
|
||||
],
|
||||
"dependencies": {
|
||||
"color": "^3.1.2",
|
||||
"array-flatten": "^3.0.0",
|
||||
"color": "^3.1.3",
|
||||
"detect-libc": "^1.0.3",
|
||||
"node-addon-api": "^3.0.2",
|
||||
"npmlog": "^4.1.2",
|
||||
"prebuild-install": "^5.3.5",
|
||||
"prebuild-install": "^6.0.0",
|
||||
"semver": "^7.3.2",
|
||||
"simple-get": "^4.0.0",
|
||||
"tar-fs": "^2.1.0",
|
||||
"tar-fs": "^2.1.1",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"async": "^3.2.0",
|
||||
"cc": "^2.0.1",
|
||||
"decompress-zip": "^0.3.2",
|
||||
"documentation": "^13.0.2",
|
||||
"documentation": "^13.1.0",
|
||||
"exif-reader": "^1.0.3",
|
||||
"icc": "^2.0.0",
|
||||
"license-checker": "^25.0.1",
|
||||
"mocha": "^8.1.1",
|
||||
"mocha": "^8.2.1",
|
||||
"mock-fs": "^4.13.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prebuild": "^10.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semistandard": "^14.2.3"
|
||||
"semistandard": "^16.0.0"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"config": {
|
||||
@@ -144,7 +146,7 @@
|
||||
"target": 3
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
|
||||
@@ -54,13 +54,13 @@ namespace sharp {
|
||||
bool AttrAsBool(Napi::Object obj, std::string attr) {
|
||||
return obj.Get(attr).As<Napi::Boolean>().Value();
|
||||
}
|
||||
std::vector<double> AttrAsRgba(Napi::Object obj, std::string attr) {
|
||||
Napi::Array background = obj.Get(attr).As<Napi::Array>();
|
||||
std::vector<double> rgba(background.Length());
|
||||
for (unsigned int i = 0; i < background.Length(); i++) {
|
||||
rgba[i] = AttrAsDouble(background, i);
|
||||
std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr) {
|
||||
Napi::Array napiArray = obj.Get(attr).As<Napi::Array>();
|
||||
std::vector<double> vectorOfDouble(napiArray.Length());
|
||||
for (unsigned int i = 0; i < napiArray.Length(); i++) {
|
||||
vectorOfDouble[i] = AttrAsDouble(napiArray, i);
|
||||
}
|
||||
return rgba;
|
||||
return vectorOfDouble;
|
||||
}
|
||||
std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr) {
|
||||
Napi::Array array = obj.Get(attr).As<Napi::Array>();
|
||||
@@ -109,7 +109,7 @@ namespace sharp {
|
||||
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
||||
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
||||
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
||||
descriptor->createBackground = AttrAsRgba(input, "createBackground");
|
||||
descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");
|
||||
}
|
||||
// Limit input images to a given number of pixels, where pixels = width * height
|
||||
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
|
||||
@@ -180,6 +180,7 @@ namespace sharp {
|
||||
case ImageType::OPENSLIDE: id = "openslide"; break;
|
||||
case ImageType::PPM: id = "ppm"; break;
|
||||
case ImageType::FITS: id = "fits"; break;
|
||||
case ImageType::EXR: id = "exr"; break;
|
||||
case ImageType::VIPS: id = "vips"; break;
|
||||
case ImageType::RAW: id = "raw"; break;
|
||||
case ImageType::UNKNOWN: id = "unknown"; break;
|
||||
@@ -210,6 +211,7 @@ namespace sharp {
|
||||
{ "openslideload", ImageType::OPENSLIDE },
|
||||
{ "ppmload", ImageType::PPM },
|
||||
{ "fitsload", ImageType::FITS },
|
||||
{ "openexrload", ImageType::EXR },
|
||||
{ "vipsload", ImageType::VIPS },
|
||||
{ "rawload", ImageType::RAW }
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace sharp {
|
||||
double AttrAsDouble(Napi::Object obj, std::string attr);
|
||||
double AttrAsDouble(Napi::Object obj, unsigned int const attr);
|
||||
bool AttrAsBool(Napi::Object obj, std::string attr);
|
||||
std::vector<double> AttrAsRgba(Napi::Object obj, std::string attr);
|
||||
std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr);
|
||||
std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr);
|
||||
|
||||
// Create an InputDescriptor instance from a Napi::Object describing an input image
|
||||
@@ -111,6 +111,7 @@ namespace sharp {
|
||||
OPENSLIDE,
|
||||
PPM,
|
||||
FITS,
|
||||
EXR,
|
||||
VIPS,
|
||||
RAW,
|
||||
UNKNOWN,
|
||||
|
||||
@@ -485,6 +485,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost);
|
||||
}
|
||||
|
||||
// Affine transform
|
||||
if (baton->affineMatrix.size() > 0) {
|
||||
std::vector<double> background;
|
||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground);
|
||||
image = image.affine(baton->affineMatrix, VImage::option()->set("background", background)
|
||||
->set("idx", baton->affineIdx)
|
||||
->set("idy", baton->affineIdy)
|
||||
->set("odx", baton->affineOdx)
|
||||
->set("ody", baton->affineOdy)
|
||||
->set("interpolate", baton->affineInterpolator));
|
||||
}
|
||||
|
||||
// Extend edges
|
||||
if (baton->extendTop > 0 || baton->extendBottom > 0 || baton->extendLeft > 0 || baton->extendRight > 0) {
|
||||
std::vector<double> background;
|
||||
@@ -1014,6 +1026,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("suffix", const_cast<char*>(suffix.data()))
|
||||
->set("angle", CalculateAngleRotation(baton->tileAngle))
|
||||
->set("background", baton->tileBackground)
|
||||
->set("centre", baton->tileCentre)
|
||||
->set("skip_blanks", baton->tileSkipBlanks);
|
||||
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
||||
// not passing anything - libvips will handle choice
|
||||
@@ -1248,7 +1261,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
// Resize options
|
||||
baton->withoutEnlargement = sharp::AttrAsBool(options, "withoutEnlargement");
|
||||
baton->position = sharp::AttrAsInt32(options, "position");
|
||||
baton->resizeBackground = sharp::AttrAsRgba(options, "resizeBackground");
|
||||
baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
|
||||
baton->kernel = sharp::AttrAsStr(options, "kernel");
|
||||
baton->fastShrinkOnLoad = sharp::AttrAsBool(options, "fastShrinkOnLoad");
|
||||
// Join Channel Options
|
||||
@@ -1261,7 +1274,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
}
|
||||
// Operators
|
||||
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
||||
baton->flattenBackground = sharp::AttrAsRgba(options, "flattenBackground");
|
||||
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
||||
baton->negate = sharp::AttrAsBool(options, "negate");
|
||||
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
||||
baton->brightness = sharp::AttrAsDouble(options, "brightness");
|
||||
@@ -1283,7 +1296,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
|
||||
baton->angle = sharp::AttrAsInt32(options, "angle");
|
||||
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
||||
baton->rotationBackground = sharp::AttrAsRgba(options, "rotationBackground");
|
||||
baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
|
||||
baton->rotateBeforePreExtract = sharp::AttrAsBool(options, "rotateBeforePreExtract");
|
||||
baton->flip = sharp::AttrAsBool(options, "flip");
|
||||
baton->flop = sharp::AttrAsBool(options, "flop");
|
||||
@@ -1291,8 +1304,15 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->extendBottom = sharp::AttrAsInt32(options, "extendBottom");
|
||||
baton->extendLeft = sharp::AttrAsInt32(options, "extendLeft");
|
||||
baton->extendRight = sharp::AttrAsInt32(options, "extendRight");
|
||||
baton->extendBackground = sharp::AttrAsRgba(options, "extendBackground");
|
||||
baton->extendBackground = sharp::AttrAsVectorOfDouble(options, "extendBackground");
|
||||
baton->extractChannel = sharp::AttrAsInt32(options, "extractChannel");
|
||||
baton->affineMatrix = sharp::AttrAsVectorOfDouble(options, "affineMatrix");
|
||||
baton->affineBackground = sharp::AttrAsVectorOfDouble(options, "affineBackground");
|
||||
baton->affineIdx = sharp::AttrAsDouble(options, "affineIdx");
|
||||
baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy");
|
||||
baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
|
||||
baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy");
|
||||
baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
|
||||
|
||||
baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
|
||||
baton->ensureAlpha = sharp::AttrAsBool(options, "ensureAlpha");
|
||||
@@ -1391,7 +1411,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
|
||||
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
|
||||
baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
|
||||
baton->tileBackground = sharp::AttrAsRgba(options, "tileBackground");
|
||||
baton->tileBackground = sharp::AttrAsVectorOfDouble(options, "tileBackground");
|
||||
baton->tileSkipBlanks = sharp::AttrAsInt32(options, "tileSkipBlanks");
|
||||
baton->tileContainer = static_cast<VipsForeignDzContainer>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_CONTAINER,
|
||||
@@ -1403,6 +1423,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->tileDepth = static_cast<VipsForeignDzDepth>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
|
||||
sharp::AttrAsStr(options, "tileDepth").data()));
|
||||
baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
|
||||
|
||||
// Force random access for certain operations
|
||||
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
|
||||
|
||||
@@ -79,6 +79,7 @@ struct PipelineBaton {
|
||||
int cropOffsetLeft;
|
||||
int cropOffsetTop;
|
||||
bool premultiplied;
|
||||
bool tileCentre;
|
||||
std::string kernel;
|
||||
bool fastShrinkOnLoad;
|
||||
double tintA;
|
||||
@@ -118,6 +119,13 @@ struct PipelineBaton {
|
||||
int extendRight;
|
||||
std::vector<double> extendBackground;
|
||||
bool withoutEnlargement;
|
||||
std::vector<double> affineMatrix;
|
||||
std::vector<double> affineBackground;
|
||||
double affineIdx;
|
||||
double affineIdy;
|
||||
double affineOdx;
|
||||
double affineOdy;
|
||||
vips::VInterpolate affineInterpolator;
|
||||
int jpegQuality;
|
||||
bool jpegProgressive;
|
||||
std::string jpegChromaSubsampling;
|
||||
@@ -230,6 +238,13 @@ struct PipelineBaton {
|
||||
extendRight(0),
|
||||
extendBackground{ 0.0, 0.0, 0.0, 255.0 },
|
||||
withoutEnlargement(false),
|
||||
affineMatrix{ 1.0, 0.0, 0.0, 1.0 },
|
||||
affineBackground{ 0.0, 0.0, 0.0, 255.0 },
|
||||
affineIdx(0),
|
||||
affineIdy(0),
|
||||
affineOdx(0),
|
||||
affineOdy(0),
|
||||
affineInterpolator(vips::VInterpolate::new_from_name("bicubic")),
|
||||
jpegQuality(80),
|
||||
jpegProgressive(false),
|
||||
jpegChromaSubsampling("4:2:0"),
|
||||
|
||||
BIN
test/fixtures/expected/affine-background-all-offsets-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
test/fixtures/expected/affine-background-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
test/fixtures/expected/affine-background-output-offsets-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/affine-bicubic-2x-upscale-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
test/fixtures/expected/affine-bilinear-2x-upscale-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
test/fixtures/expected/affine-extract-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
test/fixtures/expected/affine-extract-rotate-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
test/fixtures/expected/affine-lbb-2x-upscale-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
test/fixtures/expected/affine-nearest-2x-upscale-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
test/fixtures/expected/affine-nohalo-2x-upscale-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
test/fixtures/expected/affine-resize-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
test/fixtures/expected/affine-rotate-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
test/fixtures/expected/affine-vsqbs-2x-upscale-expected.jpg
vendored
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
test/fixtures/expected/tile_centered.jpg
vendored
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
175
test/unit/affine.js
Normal file
@@ -0,0 +1,175 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
describe('Affine transform', () => {
|
||||
describe('Invalid input', () => {
|
||||
it('Missing matrix', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine();
|
||||
});
|
||||
});
|
||||
it('Invalid 1d matrix', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine(['123', 123, 123, 123]);
|
||||
});
|
||||
});
|
||||
it('Invalid 2d matrix', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([[123, 123], [null, 123]]);
|
||||
});
|
||||
});
|
||||
it('Invalid options parameter type', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([[1, 0], [0, 1]], 'invalid options type');
|
||||
});
|
||||
});
|
||||
it('Invalid background color', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([4, 4, 4, 4], { background: 'not a color' });
|
||||
});
|
||||
});
|
||||
it('Invalid idx offset type', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([[4, 4], [4, 4]], { idx: 'invalid idx type' });
|
||||
});
|
||||
});
|
||||
it('Invalid idy offset type', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([4, 4, 4, 4], { idy: 'invalid idy type' });
|
||||
});
|
||||
});
|
||||
it('Invalid odx offset type', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([[4, 4], [4, 4]], { odx: 'invalid odx type' });
|
||||
});
|
||||
});
|
||||
it('Invalid ody offset type', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([[4, 4], [4, 4]], { ody: 'invalid ody type' });
|
||||
});
|
||||
});
|
||||
it('Invalid interpolator', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.affine([[4, 4], [4, 4]], { interpolator: 'cubic' });
|
||||
});
|
||||
});
|
||||
});
|
||||
it('Applies identity matrix', done => {
|
||||
const input = fixtures.inputJpg;
|
||||
sharp(input)
|
||||
.affine([[1, 0], [0, 1]])
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(input, data, done);
|
||||
});
|
||||
});
|
||||
it('Applies resize affine matrix', done => {
|
||||
const input = fixtures.inputJpg;
|
||||
const inputWidth = 2725;
|
||||
const inputHeight = 2225;
|
||||
sharp(input)
|
||||
.affine([[0.2, 0], [0, 1.5]])
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(input, data, done);
|
||||
assert.strictEqual(info.width, Math.ceil(inputWidth * 0.2));
|
||||
assert.strictEqual(info.height, Math.ceil(inputHeight * 1.5));
|
||||
});
|
||||
});
|
||||
it('Resizes and applies affine transform', done => {
|
||||
const input = fixtures.inputJpg;
|
||||
sharp(input)
|
||||
.resize(500, 500)
|
||||
.affine([[0.5, 1], [1, 0.5]])
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(data, fixtures.expected('affine-resize-expected.jpg'), done);
|
||||
});
|
||||
});
|
||||
it('Extracts and applies affine transform', done => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.extract({ left: 300, top: 300, width: 600, height: 600 })
|
||||
.affine([0.3, 0, -0.5, 0.3])
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(data, fixtures.expected('affine-extract-expected.jpg'), done);
|
||||
});
|
||||
});
|
||||
it('Rotates and applies affine transform', done => {
|
||||
sharp(fixtures.inputJpg320x240)
|
||||
.rotate(90)
|
||||
.affine([[-1.2, 0], [0, -1.2]])
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(data, fixtures.expected('affine-rotate-expected.jpg'), done);
|
||||
});
|
||||
});
|
||||
it('Extracts, rotates and applies affine transform', done => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.extract({ left: 1000, top: 1000, width: 200, height: 200 })
|
||||
.rotate(45, { background: 'blue' })
|
||||
.affine([[2, 1], [2, -0.5]], { background: 'red' })
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('affine-extract-rotate-expected.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
it('Applies affine transform with background color', done => {
|
||||
sharp(fixtures.inputJpg320x240)
|
||||
.rotate(180)
|
||||
.affine([[-1.5, 1.2], [-1, 1]], { background: 'red' })
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('affine-background-expected.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
it('Applies affine transform with background color and output offsets', done => {
|
||||
sharp(fixtures.inputJpg320x240)
|
||||
.rotate(180)
|
||||
.affine([[-2, 1.5], [-1, 2]], { background: 'blue', odx: 40, ody: -100 })
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('affine-background-output-offsets-expected.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
it('Applies affine transform with background color and all offsets', done => {
|
||||
sharp(fixtures.inputJpg320x240)
|
||||
.rotate(180)
|
||||
.affine([[-1.2, 1.8], [-1, 2]], { background: 'yellow', idx: 10, idy: -40, odx: 10, ody: -50 })
|
||||
.toBuffer((err, data) => {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('affine-background-all-offsets-expected.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
describe('Interpolations', () => {
|
||||
const input = fixtures.inputJpg320x240;
|
||||
const inputWidth = 320;
|
||||
const inputHeight = 240;
|
||||
for (const interp in sharp.interpolators) {
|
||||
it(`Performs 2x upscale with ${interp} interpolation`, done => {
|
||||
sharp(input)
|
||||
.affine([[2, 0], [0, 2]], { interpolator: sharp.interpolators[interp] })
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(info.width, Math.ceil(inputWidth * 2));
|
||||
assert.strictEqual(info.height, Math.ceil(inputHeight * 2));
|
||||
fixtures.assertSimilar(fixtures.expected(`affine-${sharp.interpolators[interp]}-2x-upscale-expected.jpg`), data, done);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,7 @@ const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
// Test Helpers
|
||||
var threshold = 0.001;
|
||||
const threshold = 0.001;
|
||||
function isInAcceptableRange (actual, expected) {
|
||||
return actual >= ((1 - threshold) * expected) && actual <= ((1 + threshold) * expected);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,14 @@ describe('Tile', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid center parameter value fail', function () {
|
||||
assert.throws(function () {
|
||||
sharp().tile({
|
||||
centre: 'true'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Deep Zoom layout', function (done) {
|
||||
const directory = fixtures.path('output.dzi_files');
|
||||
rimraf(directory, function () {
|
||||
@@ -765,6 +773,46 @@ describe('Tile', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Google layout with center image in tile', function (done) {
|
||||
const directory = fixtures.path('output.google_center.dzi');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.tile({
|
||||
center: true,
|
||||
layout: 'google'
|
||||
})
|
||||
.toFile(directory, function (err, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('dz', info.format);
|
||||
assert.strictEqual(2725, info.width);
|
||||
assert.strictEqual(2225, info.height);
|
||||
assert.strictEqual(3, info.channels);
|
||||
assert.strictEqual('number', typeof info.size);
|
||||
fixtures.assertSimilar(fixtures.expected('tile_centered.jpg'), fs.readFileSync(path.join(directory, '0', '0', '0.jpg')), done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Google layout with center image in tile centre', function (done) {
|
||||
const directory = fixtures.path('output.google_center.dzi');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.tile({
|
||||
centre: true,
|
||||
layout: 'google'
|
||||
})
|
||||
.toFile(directory, function (err, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('dz', info.format);
|
||||
assert.strictEqual(2725, info.width);
|
||||
assert.strictEqual(2225, info.height);
|
||||
assert.strictEqual(3, info.channels);
|
||||
assert.strictEqual('number', typeof info.size);
|
||||
fixtures.assertSimilar(fixtures.expected('tile_centered.jpg'), fs.readFileSync(path.join(directory, '0', '0', '0.jpg')), done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('IIIF layout', function (done) {
|
||||
const directory = fixtures.path('output.iiif.info');
|
||||
rimraf(directory, function () {
|
||||
|
||||