mirror of
https://github.com/lovell/sharp.git
synced 2026-02-05 06:06:18 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44a0ee3fd3 | ||
|
|
ccd51c8cbf | ||
|
|
bb7469b2d1 | ||
|
|
a2cac61209 | ||
|
|
5c19f6dd9b | ||
|
|
3d01775972 | ||
|
|
87562a5111 | ||
|
|
2829e17743 | ||
|
|
ffefbd2ecc | ||
|
|
bc8f983329 | ||
|
|
440936a699 | ||
|
|
0bc79cdb95 | ||
|
|
9a66e25f53 | ||
|
|
8370935ccf | ||
|
|
f908987f35 | ||
|
|
aea368a3a0 | ||
|
|
7ecbc20d3d | ||
|
|
cb0e2a91c4 | ||
|
|
739b317a6f | ||
|
|
a0e1c39785 | ||
|
|
85b26dab68 | ||
|
|
66f7cef253 | ||
|
|
863174f201 | ||
|
|
bcd865cc96 |
@@ -1,5 +1,5 @@
|
|||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
image_family: freebsd-14-0-snap
|
image_family: freebsd-13-2
|
||||||
|
|
||||||
task:
|
task:
|
||||||
name: FreeBSD
|
name: FreeBSD
|
||||||
@@ -9,7 +9,7 @@ task:
|
|||||||
prerequisites_script:
|
prerequisites_script:
|
||||||
- pkg update -f
|
- pkg update -f
|
||||||
- pkg upgrade -y
|
- pkg upgrade -y
|
||||||
- pkg install -y devel/pkgconf graphics/vips www/node16 www/npm
|
- pkg install -y devel/git devel/pkgconf graphics/vips www/node20 www/npm
|
||||||
install_script:
|
install_script:
|
||||||
- npm install --build-from-source --unsafe-perm
|
- npm install --build-from-source --unsafe-perm
|
||||||
test_script:
|
test_script:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@ node_modules
|
|||||||
/coverage
|
/coverage
|
||||||
test/bench/node_modules
|
test/bench/node_modules
|
||||||
test/fixtures/output*
|
test/fixtures/output*
|
||||||
|
test/fixtures/vips-properties.xml
|
||||||
test/leak/libvips.supp
|
test/leak/libvips.supp
|
||||||
test/saliency/report.json
|
test/saliency/report.json
|
||||||
test/saliency/Image*
|
test/saliency/Image*
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ and https://www.cairographics.org/operators/
|
|||||||
| [images[].input.text.align] | <code>string</code> | <code>"'left'"</code> | text alignment (`'left'`, `'centre'`, `'center'`, `'right'`). |
|
| [images[].input.text.align] | <code>string</code> | <code>"'left'"</code> | text alignment (`'left'`, `'centre'`, `'center'`, `'right'`). |
|
||||||
| [images[].input.text.justify] | <code>boolean</code> | <code>false</code> | set this to true to apply justification to the text. |
|
| [images[].input.text.justify] | <code>boolean</code> | <code>false</code> | set this to true to apply justification to the text. |
|
||||||
| [images[].input.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. |
|
| [images[].input.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. |
|
||||||
| [images[].input.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`. |
|
| [images[].input.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`. |
|
||||||
| [images[].input.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. |
|
| [images[].input.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. |
|
||||||
| [images[].blend] | <code>String</code> | <code>'over'</code> | how to blend this image with the image below. |
|
| [images[].blend] | <code>String</code> | <code>'over'</code> | how to blend this image with the image below. |
|
||||||
| [images[].gravity] | <code>String</code> | <code>'centre'</code> | gravity at which to place the overlay. |
|
| [images[].gravity] | <code>String</code> | <code>'centre'</code> | gravity at which to place the overlay. |
|
||||||
| [images[].top] | <code>Number</code> | | the pixel offset from the top edge. |
|
| [images[].top] | <code>Number</code> | | the pixel offset from the top edge. |
|
||||||
| [images[].left] | <code>Number</code> | | the pixel offset from the left edge. |
|
| [images[].left] | <code>Number</code> | | the pixel offset from the left edge. |
|
||||||
| [images[].tile] | <code>Boolean</code> | <code>false</code> | set to true to repeat the overlay image across the entire image with the given `gravity`. |
|
| [images[].tile] | <code>Boolean</code> | <code>false</code> | set to true to repeat the overlay image across the entire image with the given `gravity`. |
|
||||||
| [images[].premultiplied] | <code>Boolean</code> | <code>false</code> | set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option. |
|
| [images[].premultiplied] | <code>Boolean</code> | <code>false</code> | set to true to avoid premultiplying the image below. Equivalent to the `--premultiplied` vips option. |
|
||||||
| [images[].density] | <code>Number</code> | <code>72</code> | number representing the DPI for vector overlay image. |
|
| [images[].density] | <code>Number</code> | <code>72</code> | number representing the DPI for vector overlay image. |
|
||||||
| [images[].raw] | <code>Object</code> | | describes overlay when using raw pixel data. |
|
| [images[].raw] | <code>Object</code> | | describes overlay when using raw pixel data. |
|
||||||
| [images[].raw.width] | <code>Number</code> | | |
|
| [images[].raw.width] | <code>Number</code> | | |
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_st
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| [input] | <code>Buffer</code> \| <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Uint8ClampedArray</code> \| <code>Int8Array</code> \| <code>Uint16Array</code> \| <code>Int16Array</code> \| <code>Uint32Array</code> \| <code>Int32Array</code> \| <code>Float32Array</code> \| <code>Float64Array</code> \| <code>string</code> | | if present, can be a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or a TypedArray containing raw pixel image data, or a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file. JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present. |
|
| [input] | <code>Buffer</code> \| <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Uint8ClampedArray</code> \| <code>Int8Array</code> \| <code>Uint16Array</code> \| <code>Int16Array</code> \| <code>Uint32Array</code> \| <code>Int32Array</code> \| <code>Float32Array</code> \| <code>Float64Array</code> \| <code>string</code> | | if present, can be a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or a TypedArray containing raw pixel image data, or a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file. JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present. |
|
||||||
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
|
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
|
||||||
| [options.failOn] | <code>string</code> | <code>"'warning'"</code> | when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. |
|
| [options.failOn] | <code>string</code> | <code>"'warning'"</code> | when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), higher levels imply lower levels, invalid metadata will always abort. |
|
||||||
| [options.limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | Do not process input images where the number of pixels (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). |
|
| [options.limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | Do not process input images where the number of pixels (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). |
|
||||||
| [options.unlimited] | <code>boolean</code> | <code>false</code> | Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF). |
|
| [options.unlimited] | <code>boolean</code> | <code>false</code> | Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF). |
|
||||||
| [options.sequentialRead] | <code>boolean</code> | <code>true</code> | Set this to `false` to use random access rather than sequential read. Some operations will do this automatically. |
|
| [options.sequentialRead] | <code>boolean</code> | <code>true</code> | Set this to `false` to use random access rather than sequential read. Some operations will do this automatically. |
|
||||||
@@ -84,7 +84,7 @@ readableStream.pipe(transformer).pipe(writableStream);
|
|||||||
```
|
```
|
||||||
**Example**
|
**Example**
|
||||||
```js
|
```js
|
||||||
// Create a blank 300x200 PNG image of semi-transluent red pixels
|
// Create a blank 300x200 PNG image of semi-translucent red pixels
|
||||||
sharp({
|
sharp({
|
||||||
create: {
|
create: {
|
||||||
width: 300,
|
width: 300,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
- `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
- `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
||||||
|
- `paletteBitDepth`: Bit depth of palette-based image (GIF, PNG).
|
||||||
- `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
- `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
||||||
- `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
- `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
||||||
- `pagePrimary`: Number of the primary page in a HEIF image
|
- `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Rotate the output image by either an explicit angle
|
|||||||
or auto-orient based on the EXIF `Orientation` tag.
|
or auto-orient based on the EXIF `Orientation` tag.
|
||||||
|
|
||||||
If an angle is provided, it is converted to a valid positive degree rotation.
|
If an angle is provided, it is converted to a valid positive degree rotation.
|
||||||
For example, `-450` will produce a 270deg rotation.
|
For example, `-450` will produce a 270 degree rotation.
|
||||||
|
|
||||||
When rotating by an angle other than a multiple of 90,
|
When rotating by an angle other than a multiple of 90,
|
||||||
the background colour can be provided with the `background` option.
|
the background colour can be provided with the `background` option.
|
||||||
@@ -533,7 +533,7 @@ await sharp(rgbInput)
|
|||||||
|
|
||||||
|
|
||||||
## recomb
|
## recomb
|
||||||
Recomb the image with the specified matrix.
|
Recombine the image with the specified matrix.
|
||||||
|
|
||||||
|
|
||||||
**Throws**:
|
**Throws**:
|
||||||
@@ -556,7 +556,7 @@ sharp(input)
|
|||||||
])
|
])
|
||||||
.raw()
|
.raw()
|
||||||
.toBuffer(function(err, data, info) {
|
.toBuffer(function(err, data, info) {
|
||||||
// data contains the raw pixel data after applying the recomb
|
// data contains the raw pixel data after applying the matrix
|
||||||
// With this example input, a sepia filter has been applied
|
// With this example input, a sepia filter has been applied
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -607,7 +607,7 @@ const output = await sharp(input)
|
|||||||
```
|
```
|
||||||
**Example**
|
**Example**
|
||||||
```js
|
```js
|
||||||
// decreate brightness and saturation while also hue-rotating by 90 degrees
|
// decrease brightness and saturation while also hue-rotating by 90 degrees
|
||||||
const output = await sharp(input)
|
const output = await sharp(input)
|
||||||
.modulate({
|
.modulate({
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ Use these WebP options for output image.
|
|||||||
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression mode |
|
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression mode |
|
||||||
| [options.nearLossless] | <code>boolean</code> | <code>false</code> | use near_lossless compression mode |
|
| [options.nearLossless] | <code>boolean</code> | <code>false</code> | use near_lossless compression mode |
|
||||||
| [options.smartSubsample] | <code>boolean</code> | <code>false</code> | use high quality chroma subsampling |
|
| [options.smartSubsample] | <code>boolean</code> | <code>false</code> | use high quality chroma subsampling |
|
||||||
|
| [options.preset] | <code>string</code> | <code>"'default'"</code> | named preset for preprocessing/filtering, one of: default, photo, picture, drawing, icon, text |
|
||||||
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 6 (slowest) |
|
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 6 (slowest) |
|
||||||
| [options.loop] | <code>number</code> | <code>0</code> | number of animation iterations, use 0 for infinite animation |
|
| [options.loop] | <code>number</code> | <code>0</code> | number of animation iterations, use 0 for infinite animation |
|
||||||
| [options.delay] | <code>number</code> \| <code>Array.<number></code> | | delay(s) between animation frames (in milliseconds) |
|
| [options.delay] | <code>number</code> \| <code>Array.<number></code> | | delay(s) between animation frames (in milliseconds) |
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ When both a `width` and `height` are provided, the possible methods by which the
|
|||||||
|
|
||||||
Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
||||||
|
|
||||||
<img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.png">
|
<img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.svg">
|
||||||
|
|
||||||
When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
||||||
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
||||||
|
|||||||
@@ -165,4 +165,59 @@ const simd = sharp.simd();
|
|||||||
```js
|
```js
|
||||||
const simd = sharp.simd(false);
|
const simd = sharp.simd(false);
|
||||||
// prevent libvips from using liborc at runtime
|
// prevent libvips from using liborc at runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## block
|
||||||
|
Block libvips operations at runtime.
|
||||||
|
|
||||||
|
This is in addition to the `VIPS_BLOCK_UNTRUSTED` environment variable,
|
||||||
|
which when set will block all "untrusted" operations.
|
||||||
|
|
||||||
|
|
||||||
|
**Since**: 0.32.4
|
||||||
|
|
||||||
|
| Param | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| options | <code>Object</code> | |
|
||||||
|
| options.operation | <code>Array.<string></code> | List of libvips low-level operation names to block. |
|
||||||
|
|
||||||
|
**Example** *(Block all TIFF input.)*
|
||||||
|
```js
|
||||||
|
sharp.block({
|
||||||
|
operation: ['VipsForeignLoadTiff']
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## unblock
|
||||||
|
Unblock libvips operations at runtime.
|
||||||
|
|
||||||
|
This is useful for defining a list of allowed operations.
|
||||||
|
|
||||||
|
|
||||||
|
**Since**: 0.32.4
|
||||||
|
|
||||||
|
| Param | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| options | <code>Object</code> | |
|
||||||
|
| options.operation | <code>Array.<string></code> | List of libvips low-level operation names to unblock. |
|
||||||
|
|
||||||
|
**Example** *(Block all input except WebP from the filesystem.)*
|
||||||
|
```js
|
||||||
|
sharp.block({
|
||||||
|
operation: ['VipsForeignLoad']
|
||||||
|
});
|
||||||
|
sharp.unblock({
|
||||||
|
operation: ['VipsForeignLoadWebpFile']
|
||||||
|
});
|
||||||
|
```
|
||||||
|
**Example** *(Block all input except JPEG and PNG from a Buffer or Stream.)*
|
||||||
|
```js
|
||||||
|
sharp.block({
|
||||||
|
operation: ['VipsForeignLoad']
|
||||||
|
});
|
||||||
|
sharp.unblock({
|
||||||
|
operation: ['VipsForeignLoadJpegBuffer', 'VipsForeignLoadPngBuffer']
|
||||||
|
});
|
||||||
```
|
```
|
||||||
@@ -2,7 +2,46 @@
|
|||||||
|
|
||||||
## v0.32 - *flow*
|
## v0.32 - *flow*
|
||||||
|
|
||||||
Requires libvips v8.14.2
|
Requires libvips v8.14.4
|
||||||
|
|
||||||
|
### v0.32.5 - 15th August 2023
|
||||||
|
|
||||||
|
* Upgrade to libvips v8.14.4 for upstream bug fixes.
|
||||||
|
|
||||||
|
* TypeScript: Add missing `WebpPresetEnum` to definitions.
|
||||||
|
[#3748](https://github.com/lovell/sharp/pull/3748)
|
||||||
|
[@pilotso11](https://github.com/pilotso11)
|
||||||
|
|
||||||
|
* Ensure compilation using musl v1.2.4.
|
||||||
|
[#3755](https://github.com/lovell/sharp/pull/3755)
|
||||||
|
[@kleisauke](https://github.com/kleisauke)
|
||||||
|
|
||||||
|
* Ensure resize with a `fit` of `inside` respects 90/270 degree rotation.
|
||||||
|
[#3756](https://github.com/lovell/sharp/issues/3756)
|
||||||
|
|
||||||
|
* TypeScript: Ensure `minSize` property of `WebpOptions` is boolean.
|
||||||
|
[#3758](https://github.com/lovell/sharp/pull/3758)
|
||||||
|
[@sho-xizz](https://github.com/sho-xizz)
|
||||||
|
|
||||||
|
* Ensure `withMetadata` adds default sRGB profile.
|
||||||
|
[#3761](https://github.com/lovell/sharp/issues/3761)
|
||||||
|
|
||||||
|
### v0.32.4 - 21st July 2023
|
||||||
|
|
||||||
|
* Upgrade to libvips v8.14.3 for upstream bug fixes.
|
||||||
|
|
||||||
|
* Expose ability to (un)block low-level libvips operations by name.
|
||||||
|
|
||||||
|
* Prebuilt binaries: restore support for tile-based output.
|
||||||
|
[#3581](https://github.com/lovell/sharp/issues/3581)
|
||||||
|
|
||||||
|
### v0.32.3 - 14th July 2023
|
||||||
|
|
||||||
|
* Expose `preset` option for WebP output.
|
||||||
|
[#3639](https://github.com/lovell/sharp/issues/3639)
|
||||||
|
|
||||||
|
* Ensure decoding remains sequential for all operations (regression in 0.32.2).
|
||||||
|
[#3725](https://github.com/lovell/sharp/issues/3725)
|
||||||
|
|
||||||
### v0.32.2 - 11th July 2023
|
### v0.32.2 - 11th July 2023
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
61
docs/image/api-resize-fit.svg
Normal file
61
docs/image/api-resize-fit.svg
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="998" height="243" viewBox="0 0 998 243">
|
||||||
|
<defs>
|
||||||
|
<g id="placeholder">
|
||||||
|
<rect width="180" height="128" fill="#64bed8"/>
|
||||||
|
<circle cx="61.1" cy="36.8" r="19.3" fill="#ffefa9"/>
|
||||||
|
<circle cx="61.1" cy="36.8" r="18.1" fill="#fdda42"/>
|
||||||
|
<path d="m67.2 34.7 15.2 46L90 57.9l30.4 38 7.8-15.2 7.5 15.4H44z" fill="#6a696f"/>
|
||||||
|
<path d="m82.4 80.7-15.2-46-.3 69h22.9z" fill="#474749"/>
|
||||||
|
<path d="m90.1 58 12.2 15 18.2 23-13.9.1z" fill="#474749"/>
|
||||||
|
<path d="M135.8 96H131l-2.8-15.3z" fill="#474749"/>
|
||||||
|
<path d="M35.2 96h107.1c0 1.7-1.4 3.2-3.2 3.2H38.4a3.2 3.2 0 0 1-3.2-3.2z" fill="#b9c861"/>
|
||||||
|
<path d="m67.2 34.7-.1 31-6.2-3-5.3 2.7z" fill="#fff"/>
|
||||||
|
<path d="m67.2 34.7 7.6 23-7.7 8z" fill="#b3b1b4"/>
|
||||||
|
<rect width="30.8" height="7.7" x="71.1" y="27.2" rx="2.8" ry="4.1" fill="#fff"/>
|
||||||
|
<rect width="30.8" height="7.7" x="82.2" y="34.8" rx="2.8" ry="4.1" fill="#fff"/>
|
||||||
|
<rect width="30.8" height="7.7" x="36.2" y="19.6" rx="2.8" ry="4.1" fill="#fff"/>
|
||||||
|
<path d="m89.6 72.8-7.2 7.9L90 57.9l10 23z" fill="#fff"/>
|
||||||
|
<path d="m90.1 58 10 23 2.2-8z" fill="#b3b1b4"/>
|
||||||
|
<path d="M131.2 85.2 137 68l9 17.2-8 6z" fill="#8da128"/>
|
||||||
|
<rect width="109.4" height="6.8" x="33.9" y="99.1" rx="13.2" ry="11.4" fill="#22b0d6"/>
|
||||||
|
<path d="m137 68-5.8 17.2 6.8 6.1.3-13.7z" fill="#727d2e"/>
|
||||||
|
<rect width="83.3" height="6.8" x="50.8" y="103.6" rx="10" ry="11.4" fill="#22b0d6"/>
|
||||||
|
<rect width=".7" height="18.4" x="138" y="77.6" fill="#585657"/>
|
||||||
|
<rect width=".5" height="5.2" x="2" y="-161.3" fill="#585657" transform="rotate(120)"/>
|
||||||
|
<rect width=".5" height="5.3" x="5.5" y="-163.3" fill="#585657" transform="rotate(120)"/>
|
||||||
|
<rect width=".5" height="4.8" x="-142.4" y="77.7" fill="#585657" transform="rotate(240)"/>
|
||||||
|
<rect width=".5" height="5.1" x="-146" y="75.6" fill="#585657" transform="rotate(240)"/>
|
||||||
|
</g>
|
||||||
|
<pattern id="img" height="100%" width="100%" viewBox="0 0 180 128">
|
||||||
|
<use xlink:href="#placeholder"/>
|
||||||
|
</pattern>
|
||||||
|
<pattern id="img-fill" width="100%" height="100%" viewBox="0 0 180 128" preserveAspectRatio="none">
|
||||||
|
<use xlink:href="#placeholder"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect x="0" y="0" width="998" height="243" fill="#ddd"/>
|
||||||
|
<g id="cover">
|
||||||
|
<rect x="22" y="28" width="180" height="132" fill="url(#img)"/>
|
||||||
|
<rect x="48" y="30" width="128" height="128" fill="none" stroke="#000" stroke-width="4"/>
|
||||||
|
<text x="112" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold">cover</text>
|
||||||
|
</g>
|
||||||
|
<g id="contain">
|
||||||
|
<rect x="240" y="30" width="128" height="128" fill="url(#img)" stroke="#000" stroke-width="4"/>
|
||||||
|
<text x="304" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold" fill="#555">contain</text>
|
||||||
|
</g>
|
||||||
|
<g id="fill">
|
||||||
|
<rect x="432" y="30" width="128" height="128" fill="url(#img-fill)" stroke="#000" stroke-width="4"/>
|
||||||
|
<text x="496" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold">fill</text>
|
||||||
|
</g>
|
||||||
|
<g id="inside">
|
||||||
|
<rect x="624" y="48" width="128" height="92" fill="url(#img)" stroke="#000" stroke-width="4"/>
|
||||||
|
<rect x="624" y="30" width="128" height="128" fill="none" stroke="#000" stroke-width="4" stroke-dasharray="12 4" stroke-dashoffset="6"/>
|
||||||
|
<text x="688" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold" fill="#555">inside</text>
|
||||||
|
</g>
|
||||||
|
<g id="outside">
|
||||||
|
<rect x="792" y="30" width="176" height="128" fill="url(#img)" stroke="#000" stroke-width="4"/>
|
||||||
|
<rect x="816" y="30" width="128" height="128" fill="none" stroke="#000" stroke-width="4" stroke-dasharray="12 4" stroke-dashoffset="-2"/>
|
||||||
|
<text x="880" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold">outside</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
File diff suppressed because one or more lines are too long
@@ -108,14 +108,14 @@ const blend = {
|
|||||||
* @param {string} [images[].input.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
|
* @param {string} [images[].input.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
|
||||||
* @param {boolean} [images[].input.text.justify=false] - set this to true to apply justification to the text.
|
* @param {boolean} [images[].input.text.justify=false] - set this to true to apply justification to the text.
|
||||||
* @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
|
* @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
|
||||||
* @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
|
* @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`.
|
||||||
* @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
|
* @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
|
||||||
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
|
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
|
||||||
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
|
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
|
||||||
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
||||||
* @param {Number} [images[].left] - the pixel offset from the left edge.
|
* @param {Number} [images[].left] - the pixel offset from the left edge.
|
||||||
* @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
|
* @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
|
||||||
* @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option.
|
* @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultiplying the image below. Equivalent to the `--premultiplied` vips option.
|
||||||
* @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
|
* @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
|
||||||
* @param {Object} [images[].raw] - describes overlay when using raw pixel data.
|
* @param {Object} [images[].raw] - describes overlay when using raw pixel data.
|
||||||
* @param {Number} [images[].raw.width]
|
* @param {Number} [images[].raw.width]
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* readableStream.pipe(transformer).pipe(writableStream);
|
* readableStream.pipe(transformer).pipe(writableStream);
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Create a blank 300x200 PNG image of semi-transluent red pixels
|
* // Create a blank 300x200 PNG image of semi-translucent red pixels
|
||||||
* sharp({
|
* sharp({
|
||||||
* create: {
|
* create: {
|
||||||
* width: 300,
|
* width: 300,
|
||||||
@@ -122,7 +122,7 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
||||||
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||||
* @param {Object} [options] - if present, is an Object with optional attributes.
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
||||||
* @param {string} [options.failOn='warning'] - when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort.
|
* @param {string} [options.failOn='warning'] - when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), higher levels imply lower levels, invalid metadata will always abort.
|
||||||
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
|
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
|
||||||
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
||||||
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
||||||
@@ -291,6 +291,7 @@ const Sharp = function (input, options) {
|
|||||||
webpLossless: false,
|
webpLossless: false,
|
||||||
webpNearLossless: false,
|
webpNearLossless: false,
|
||||||
webpSmartSubsample: false,
|
webpSmartSubsample: false,
|
||||||
|
webpPreset: 'default',
|
||||||
webpEffort: 4,
|
webpEffort: 4,
|
||||||
webpMinSize: false,
|
webpMinSize: false,
|
||||||
webpMixed: false,
|
webpMixed: false,
|
||||||
|
|||||||
13
lib/index.d.ts
vendored
13
lib/index.d.ts
vendored
@@ -1124,9 +1124,11 @@ declare namespace sharp {
|
|||||||
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
|
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
|
||||||
effort?: number | undefined;
|
effort?: number | undefined;
|
||||||
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
|
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
|
||||||
minSize?: number;
|
minSize?: boolean;
|
||||||
/** Allow mixture of lossy and lossless animation frames (slow) (optional, default false) */
|
/** Allow mixture of lossy and lossless animation frames (slow) (optional, default false) */
|
||||||
mixed?: boolean;
|
mixed?: boolean;
|
||||||
|
/** Preset options: one of default, photo, picture, drawing, icon, text (optional, default 'default') */
|
||||||
|
preset?: keyof PresetEnum | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AvifOptions extends OutputOptions {
|
interface AvifOptions extends OutputOptions {
|
||||||
@@ -1476,6 +1478,15 @@ declare namespace sharp {
|
|||||||
lanczos3: 'lanczos3';
|
lanczos3: 'lanczos3';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PresetEnum {
|
||||||
|
default: 'default';
|
||||||
|
picture: 'picture';
|
||||||
|
photo: 'photo';
|
||||||
|
drawing: 'drawing';
|
||||||
|
icon: 'icon';
|
||||||
|
text: 'text';
|
||||||
|
}
|
||||||
|
|
||||||
interface BoolEnum {
|
interface BoolEnum {
|
||||||
and: 'and';
|
and: 'and';
|
||||||
or: 'or';
|
or: 'or';
|
||||||
|
|||||||
@@ -432,6 +432,7 @@ function _isStreamInput () {
|
|||||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
* - `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
* - `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
||||||
|
* - `paletteBitDepth`: Bit depth of palette-based image (GIF, PNG).
|
||||||
* - `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
* - `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
||||||
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
||||||
* - `pagePrimary`: Number of the primary page in a HEIF image
|
* - `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const is = require('./is');
|
|||||||
* or auto-orient based on the EXIF `Orientation` tag.
|
* or auto-orient based on the EXIF `Orientation` tag.
|
||||||
*
|
*
|
||||||
* If an angle is provided, it is converted to a valid positive degree rotation.
|
* If an angle is provided, it is converted to a valid positive degree rotation.
|
||||||
* For example, `-450` will produce a 270deg rotation.
|
* For example, `-450` will produce a 270 degree rotation.
|
||||||
*
|
*
|
||||||
* When rotating by an angle other than a multiple of 90,
|
* When rotating by an angle other than a multiple of 90,
|
||||||
* the background colour can be provided with the `background` option.
|
* the background colour can be provided with the `background` option.
|
||||||
@@ -772,7 +772,7 @@ function linear (a, b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recomb the image with the specified matrix.
|
* Recombine the image with the specified matrix.
|
||||||
*
|
*
|
||||||
* @since 0.21.1
|
* @since 0.21.1
|
||||||
*
|
*
|
||||||
@@ -785,7 +785,7 @@ function linear (a, b) {
|
|||||||
* ])
|
* ])
|
||||||
* .raw()
|
* .raw()
|
||||||
* .toBuffer(function(err, data, info) {
|
* .toBuffer(function(err, data, info) {
|
||||||
* // data contains the raw pixel data after applying the recomb
|
* // data contains the raw pixel data after applying the matrix
|
||||||
* // With this example input, a sepia filter has been applied
|
* // With this example input, a sepia filter has been applied
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
@@ -842,7 +842,7 @@ function recomb (inputMatrix) {
|
|||||||
* .toBuffer();
|
* .toBuffer();
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // decreate brightness and saturation while also hue-rotating by 90 degrees
|
* // decrease brightness and saturation while also hue-rotating by 90 degrees
|
||||||
* const output = await sharp(input)
|
* const output = await sharp(input)
|
||||||
* .modulate({
|
* .modulate({
|
||||||
* brightness: 0.5,
|
* brightness: 0.5,
|
||||||
|
|||||||
@@ -483,6 +483,7 @@ function png (options) {
|
|||||||
* @param {boolean} [options.lossless=false] - use lossless compression mode
|
* @param {boolean} [options.lossless=false] - use lossless compression mode
|
||||||
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
|
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
|
||||||
* @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
|
* @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
|
||||||
|
* @param {string} [options.preset='default'] - named preset for preprocessing/filtering, one of: default, photo, picture, drawing, icon, text
|
||||||
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
|
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
|
||||||
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
||||||
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
||||||
@@ -517,6 +518,13 @@ function webp (options) {
|
|||||||
if (is.defined(options.smartSubsample)) {
|
if (is.defined(options.smartSubsample)) {
|
||||||
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
|
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.preset)) {
|
||||||
|
if (is.string(options.preset) && is.inArray(options.preset, ['default', 'photo', 'picture', 'drawing', 'icon', 'text'])) {
|
||||||
|
this.options.webpPreset = options.preset;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('preset', 'one of: default, photo, picture, drawing, icon, text', options.preset);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (is.defined(options.effort)) {
|
if (is.defined(options.effort)) {
|
||||||
if (is.integer(options.effort) && is.inRange(options.effort, 0, 6)) {
|
if (is.integer(options.effort) && is.inRange(options.effort, 0, 6)) {
|
||||||
this.options.webpEffort = options.effort;
|
this.options.webpEffort = options.effort;
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ function isResizeExpected (options) {
|
|||||||
*
|
*
|
||||||
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
||||||
*
|
*
|
||||||
* <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.png">
|
* <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.svg">
|
||||||
*
|
*
|
||||||
* When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
* When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
||||||
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
||||||
|
|||||||
@@ -202,6 +202,72 @@ function simd (simd) {
|
|||||||
}
|
}
|
||||||
simd(true);
|
simd(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block libvips operations at runtime.
|
||||||
|
*
|
||||||
|
* This is in addition to the `VIPS_BLOCK_UNTRUSTED` environment variable,
|
||||||
|
* which when set will block all "untrusted" operations.
|
||||||
|
*
|
||||||
|
* @since 0.32.4
|
||||||
|
*
|
||||||
|
* @example <caption>Block all TIFF input.</caption>
|
||||||
|
* sharp.block({
|
||||||
|
* operation: ['VipsForeignLoadTiff']
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {Array<string>} options.operation - List of libvips low-level operation names to block.
|
||||||
|
*/
|
||||||
|
function block (options) {
|
||||||
|
if (is.object(options)) {
|
||||||
|
if (Array.isArray(options.operation) && options.operation.every(is.string)) {
|
||||||
|
sharp.block(options.operation, true);
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('operation', 'Array<string>', options.operation);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('options', 'object', options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblock libvips operations at runtime.
|
||||||
|
*
|
||||||
|
* This is useful for defining a list of allowed operations.
|
||||||
|
*
|
||||||
|
* @since 0.32.4
|
||||||
|
*
|
||||||
|
* @example <caption>Block all input except WebP from the filesystem.</caption>
|
||||||
|
* sharp.block({
|
||||||
|
* operation: ['VipsForeignLoad']
|
||||||
|
* });
|
||||||
|
* sharp.unblock({
|
||||||
|
* operation: ['VipsForeignLoadWebpFile']
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @example <caption>Block all input except JPEG and PNG from a Buffer or Stream.</caption>
|
||||||
|
* sharp.block({
|
||||||
|
* operation: ['VipsForeignLoad']
|
||||||
|
* });
|
||||||
|
* sharp.unblock({
|
||||||
|
* operation: ['VipsForeignLoadJpegBuffer', 'VipsForeignLoadPngBuffer']
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {Array<string>} options.operation - List of libvips low-level operation names to unblock.
|
||||||
|
*/
|
||||||
|
function unblock (options) {
|
||||||
|
if (is.object(options)) {
|
||||||
|
if (Array.isArray(options.operation) && options.operation.every(is.string)) {
|
||||||
|
sharp.block(options.operation, false);
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('operation', 'Array<string>', options.operation);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('options', 'object', options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorate the Sharp class with utility-related functions.
|
* Decorate the Sharp class with utility-related functions.
|
||||||
* @private
|
* @private
|
||||||
@@ -216,4 +282,6 @@ module.exports = function (Sharp) {
|
|||||||
Sharp.versions = versions;
|
Sharp.versions = versions;
|
||||||
Sharp.vendor = vendor;
|
Sharp.vendor = vendor;
|
||||||
Sharp.queue = queue;
|
Sharp.queue = queue;
|
||||||
|
Sharp.block = block;
|
||||||
|
Sharp.unblock = unblock;
|
||||||
};
|
};
|
||||||
|
|||||||
28
package.json
28
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
||||||
"version": "0.32.2",
|
"version": "0.32.5",
|
||||||
"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": [
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"detect-libc": "^2.0.1",
|
"detect-libc": "^2.0.2",
|
||||||
"node-addon-api": "^6.1.0",
|
"node-addon-api": "^6.1.0",
|
||||||
"prebuild-install": "^7.1.1",
|
"prebuild-install": "^7.1.1",
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.5.4",
|
||||||
@@ -159,19 +159,19 @@
|
|||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
"config": {
|
||||||
"libvips": "8.14.2",
|
"libvips": "8.14.4",
|
||||||
"integrity": {
|
"integrity": {
|
||||||
"darwin-arm64v8": "sha512-eUuxg6H0tXgX4z2lsaGtZ4cbPAm7yoFgkvPDd4csxoiVt+QUB25pEJwiXw7oB53VlBFIp3O8lbydSFS5zH8MQQ==",
|
"darwin-arm64v8": "sha512-jZt5+ZBQzdloop9z/XlOAy8jHxD+ZGt3J8YUm1g3njjjKmZ/RmM9r6QAeLLILe67ATHaaAtmCil37fDc400OrQ==",
|
||||||
"darwin-x64": "sha512-cMT4v76IgzSR0VoXqLk/yftRyzMEZ+SBVMLzXCgqP/lmnYisrpmHHNqrWnoZbUUBXbPXLn6KMultYOJHe/c9ZQ==",
|
"darwin-x64": "sha512-Mhpr8n8CjrU+u5K9YLucmkCgwtJGexECLOZejPfqM8CiOMerowR0wJTuSt9WTOtb9qGOL/ndybfrymsw+YH8PA==",
|
||||||
"linux-arm64v8": "sha512-OcDJ/ly80pxwaKnw0W91sSvZczPtWsjmzrY/+6NMiQZT84LkmeaRuwErbHhorKDxnl7iZuNn9Uj5V25Xmj+LDQ==",
|
"linux-arm64v8": "sha512-k2PiOOv8amzS4m5jc4Vceozv8h041IoyHL/1s0Rj290jg3w6BUJL3V+TLwKUPM35i7rV5rm14gtnGZ7qKENdmA==",
|
||||||
"linux-armv6": "sha512-hk2ohSOYTJEtVQxEQFyQ+tuayKpYqx6NiXa7AE+8MF+yscxt+g+mLJ7TjDqtmb4ttFGH4IVfsEfU2YXIqWqkpg==",
|
"linux-armv6": "sha512-CuPTo50owR8P+BCCcWk1tF4qB3XSAaHeaIzSanJM/v9zBZfUfMGI0OLv+ByyHCL3BE2CbGXaSXhuEVw2JQ08Sw==",
|
||||||
"linux-armv7": "sha512-/5Ci2Cd+yLZmTaEt9lVJ89elxX3RMJpps0ESjj43X40yrwka51QfXeg1QV38uNzZpCDIZkrbXZK0lyKldjpLuA==",
|
"linux-armv7": "sha512-CvD6fMy9PkZk1m2UPTWDcFfcD4qFA3RALyAWIih8ftOY9ksI3Y4uz6c0ML+ixBl0hqQK3WEg6+ac5TGDjZbbYA==",
|
||||||
"linux-x64": "sha512-wjCKmWfBb0uz1UB7rPDLvO0s+VWuoAY/Vv/YGCRFEQUkdSLQUgHExrOMMbOM3FleuYfQqznDYCXXphkl7X44+w==",
|
"linux-x64": "sha512-vqoV61ka2hBYQ5582nQlyUcVPtItu927mng9RUU9nyO4Wt50z9nNT/pfcYEfF2jkBNW9JaiMaj6bENHgxA6mMg==",
|
||||||
"linuxmusl-arm64v8": "sha512-QtD2n90yi+rLE65C0gksFUU5uMUFPICI/pS3A0bgthpIcoCejAOYs3ZjVWpZbHQuV/lWahIUYO78MB9CzY860A==",
|
"linuxmusl-arm64v8": "sha512-iCyl0y/qxdvgGidsYn11R8d4TEcU92uYHtYI8FSHyUobZw/9i2y3189PUTQ/fw44oqaBzTR3p9NF2eP6aLT9gQ==",
|
||||||
"linuxmusl-x64": "sha512-TokQ/ETCJAsPYuxIMOPYDp25rlcwtpmIMtRUR9PB75TmZEJe7abRfCEInIPYeD8F/HxxnJSLiEdlbn1z1Jfzng==",
|
"linuxmusl-x64": "sha512-qj7IUqWUqCtxECpgNp4E1NcIbsNe1ujzBuJcnnQot7GZOuPUhI5N6ZUhozmh6LfbGFdBZpPc/JFh1eDZ0IEpbQ==",
|
||||||
"win32-arm64v8": "sha512-IIuj4EAgLqEVAoOuYH79C61a7TcJXlU/RBwk+5JsGWc2mr4J/Ar5J01e6XBvU4Lu3eqcU+3GPaACZEa1511buA==",
|
"win32-arm64v8": "sha512-VRi7fpE9Kb3xQGcNmPPTJnWGAEUMq+YOq9abpaIIB2r3Ax327/7wHS7o2ezD6zQKdxIX6gODC5io/hReIJ9Jnw==",
|
||||||
"win32-ia32": "sha512-CsZi7lrReX3B6tmYgOGJ0IiAfcN5APDC6l+3gdosxfTfwpLLO+jXaSmyNwIGeMqrdgckG/gwwc+IrUZmkmjJ/A==",
|
"win32-ia32": "sha512-EnvtU7Q6+pjl5/Y1/UngCFDM2CSqpYWVDwY03ilUKSuqTeDKTJYyus0rJ+n6p4nmdjJlVdhYlkvpy8kkEAtDHg==",
|
||||||
"win32-x64": "sha512-J7znmNKUK4ZKo6SnSnEtzT1xRAwvkGXxIx9/QihAadu1TFdS06yNhcENmwC4973+KZBlAdVpWbZ8sLrEoWkdCA=="
|
"win32-x64": "sha512-fCl/KQuSijVYC8hULWbff8Mfuh3vjjdz4j5p73VgdLP6aZUrHctbhBvEIe0aQ8HpmcGdBnATX5pXUQ4GDl3mwQ=="
|
||||||
},
|
},
|
||||||
"runtime": "napi",
|
"runtime": "napi",
|
||||||
"target": 7
|
"target": 7
|
||||||
|
|||||||
@@ -964,12 +964,7 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
||||||
Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction) {
|
Canvas canvas, bool withoutEnlargement, bool withoutReduction) {
|
||||||
if (swap && canvas != Canvas::IGNORE_ASPECT) {
|
|
||||||
// Swap input width and height when requested.
|
|
||||||
std::swap(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
double hshrink = 1.0;
|
double hshrink = 1.0;
|
||||||
double vshrink = 1.0;
|
double vshrink = 1.0;
|
||||||
|
|
||||||
@@ -1035,4 +1030,13 @@ namespace sharp {
|
|||||||
return std::make_pair(hshrink, vshrink);
|
return std::make_pair(hshrink, vshrink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ensure decoding remains sequential.
|
||||||
|
*/
|
||||||
|
VImage StaySequential(VImage image, VipsAccess access, bool condition) {
|
||||||
|
if (access == VIPS_ACCESS_SEQUENTIAL && condition) {
|
||||||
|
return image.copy_memory();
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|||||||
16
src/common.h
16
src/common.h
@@ -15,8 +15,8 @@
|
|||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 14) || \
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 14) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 2)
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 4)
|
||||||
#error "libvips version 8.14.2+ is required - please see https://sharp.pixelplumbing.com/install"
|
#error "libvips version 8.14.4+ 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)))
|
||||||
@@ -362,13 +362,15 @@ namespace sharp {
|
|||||||
VImage EnsureAlpha(VImage image, double const value);
|
VImage EnsureAlpha(VImage image, double const value);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calculate the shrink factor, taking into account auto-rotate, the canvas
|
Calculate the horizontal and vertical shrink factors, taking the canvas mode into account.
|
||||||
mode, and so on. The hshrink/vshrink are the amount to shrink the input
|
|
||||||
image axes by in order for the output axes (ie. after rotation) to match
|
|
||||||
the required thumbnail width/height and canvas mode.
|
|
||||||
*/
|
*/
|
||||||
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
||||||
Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction);
|
Canvas canvas, bool withoutEnlargement, bool withoutReduction);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ensure decoding remains sequential.
|
||||||
|
*/
|
||||||
|
VImage StaySequential(VImage image, VipsAccess access, bool condition = TRUE);
|
||||||
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|
||||||
|
|||||||
@@ -20,18 +20,15 @@
|
|||||||
#include "operations.h"
|
#include "operations.h"
|
||||||
#include "pipeline.h"
|
#include "pipeline.h"
|
||||||
|
|
||||||
#if defined(WIN32)
|
#ifdef _WIN32
|
||||||
#define STAT64_STRUCT __stat64
|
#define STAT64_STRUCT __stat64
|
||||||
#define STAT64_FUNCTION _stat64
|
#define STAT64_FUNCTION _stat64
|
||||||
#elif defined(__APPLE__)
|
#elif defined(_LARGEFILE64_SOURCE)
|
||||||
#define STAT64_STRUCT stat
|
|
||||||
#define STAT64_FUNCTION stat
|
|
||||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
|
|
||||||
#define STAT64_STRUCT stat
|
|
||||||
#define STAT64_FUNCTION stat
|
|
||||||
#else
|
|
||||||
#define STAT64_STRUCT stat64
|
#define STAT64_STRUCT stat64
|
||||||
#define STAT64_FUNCTION stat64
|
#define STAT64_FUNCTION stat64
|
||||||
|
#else
|
||||||
|
#define STAT64_STRUCT stat
|
||||||
|
#define STAT64_FUNCTION stat
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class PipelineWorker : public Napi::AsyncWorker {
|
class PipelineWorker : public Napi::AsyncWorker {
|
||||||
@@ -56,6 +53,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
vips::VImage image;
|
vips::VImage image;
|
||||||
sharp::ImageType inputImageType;
|
sharp::ImageType inputImageType;
|
||||||
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
||||||
|
VipsAccess access = baton->input->access;
|
||||||
image = sharp::EnsureColourspace(image, baton->colourspaceInput);
|
image = sharp::EnsureColourspace(image, baton->colourspaceInput);
|
||||||
|
|
||||||
int nPages = baton->input->pages;
|
int nPages = baton->input->pages;
|
||||||
@@ -79,9 +77,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Rotate and flip image according to Exif orientation
|
// Rotate and flip image according to Exif orientation
|
||||||
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
||||||
image = sharp::RemoveExifOrientation(image);
|
image = sharp::RemoveExifOrientation(image);
|
||||||
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL && (autoRotation != VIPS_ANGLE_D0 || autoFlip)) {
|
|
||||||
image = image.copy_memory();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
rotation = CalculateAngleRotation(baton->angle);
|
rotation = CalculateAngleRotation(baton->angle);
|
||||||
}
|
}
|
||||||
@@ -93,6 +88,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
baton->rotationAngle != 0.0);
|
baton->rotationAngle != 0.0);
|
||||||
|
|
||||||
if (shouldRotateBefore) {
|
if (shouldRotateBefore) {
|
||||||
|
image = sharp::StaySequential(image, access,
|
||||||
|
rotation != VIPS_ANGLE_D0 ||
|
||||||
|
autoRotation != VIPS_ANGLE_D0 ||
|
||||||
|
autoFlip ||
|
||||||
|
baton->flip ||
|
||||||
|
baton->rotationAngle != 0.0);
|
||||||
|
|
||||||
if (autoRotation != VIPS_ANGLE_D0) {
|
if (autoRotation != VIPS_ANGLE_D0) {
|
||||||
image = image.rot(autoRotation);
|
image = image.rot(autoRotation);
|
||||||
autoRotation = VIPS_ANGLE_D0;
|
autoRotation = VIPS_ANGLE_D0;
|
||||||
@@ -126,6 +128,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Trim
|
// Trim
|
||||||
if (baton->trimThreshold > 0.0) {
|
if (baton->trimThreshold > 0.0) {
|
||||||
MultiPageUnsupported(nPages, "Trim");
|
MultiPageUnsupported(nPages, "Trim");
|
||||||
|
image = sharp::StaySequential(image, access);
|
||||||
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold);
|
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold);
|
||||||
baton->trimOffsetLeft = image.xoffset();
|
baton->trimOffsetLeft = image.xoffset();
|
||||||
baton->trimOffsetTop = image.yoffset();
|
baton->trimOffsetTop = image.yoffset();
|
||||||
@@ -154,15 +157,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
int targetResizeWidth = baton->width;
|
int targetResizeWidth = baton->width;
|
||||||
int targetResizeHeight = baton->height;
|
int targetResizeHeight = baton->height;
|
||||||
|
|
||||||
// Swap input output width and height when rotating by 90 or 270 degrees
|
// When auto-rotating by 90 or 270 degrees, swap the target width and
|
||||||
bool swap = !baton->rotateBeforePreExtract &&
|
// height to ensure the behavior aligns with how it would have been if
|
||||||
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270 ||
|
// the rotation had taken place *before* resizing.
|
||||||
autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270);
|
if (!baton->rotateBeforePreExtract &&
|
||||||
|
(autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270)) {
|
||||||
|
std::swap(targetResizeWidth, targetResizeHeight);
|
||||||
|
}
|
||||||
|
|
||||||
// Shrink to pageHeight, so we work for multi-page images
|
// Shrink to pageHeight, so we work for multi-page images
|
||||||
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
||||||
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
||||||
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
baton->canvas, baton->withoutEnlargement, baton->withoutReduction);
|
||||||
|
|
||||||
// The jpeg preload shrink.
|
// The jpeg preload shrink.
|
||||||
int jpegShrinkOnLoad = 1;
|
int jpegShrinkOnLoad = 1;
|
||||||
@@ -212,7 +218,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// pdfload* and svgload*
|
// pdfload* and svgload*
|
||||||
if (jpegShrinkOnLoad > 1) {
|
if (jpegShrinkOnLoad > 1) {
|
||||||
vips::VOption *option = VImage::option()
|
vips::VOption *option = VImage::option()
|
||||||
->set("access", baton->input->access)
|
->set("access", access)
|
||||||
->set("shrink", jpegShrinkOnLoad)
|
->set("shrink", jpegShrinkOnLoad)
|
||||||
->set("unlimited", baton->input->unlimited)
|
->set("unlimited", baton->input->unlimited)
|
||||||
->set("fail_on", baton->input->failOn);
|
->set("fail_on", baton->input->failOn);
|
||||||
@@ -227,7 +233,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
} else if (scale != 1.0) {
|
} else if (scale != 1.0) {
|
||||||
vips::VOption *option = VImage::option()
|
vips::VOption *option = VImage::option()
|
||||||
->set("access", baton->input->access)
|
->set("access", access)
|
||||||
->set("scale", scale)
|
->set("scale", scale)
|
||||||
->set("fail_on", baton->input->failOn);
|
->set("fail_on", baton->input->failOn);
|
||||||
if (inputImageType == sharp::ImageType::WEBP) {
|
if (inputImageType == sharp::ImageType::WEBP) {
|
||||||
@@ -296,7 +302,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Shrink to pageHeight, so we work for multi-page images
|
// Shrink to pageHeight, so we work for multi-page images
|
||||||
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
||||||
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
||||||
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
baton->canvas, baton->withoutEnlargement, baton->withoutReduction);
|
||||||
|
|
||||||
int targetHeight = static_cast<int>(std::rint(static_cast<double>(pageHeight) / vshrink));
|
int targetHeight = static_cast<int>(std::rint(static_cast<double>(pageHeight) / vshrink));
|
||||||
int targetPageHeight = targetHeight;
|
int targetPageHeight = targetHeight;
|
||||||
@@ -379,6 +385,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("kernel", baton->kernel));
|
->set("kernel", baton->kernel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
image = sharp::StaySequential(image, access,
|
||||||
|
autoRotation != VIPS_ANGLE_D0 ||
|
||||||
|
baton->flip ||
|
||||||
|
autoFlip ||
|
||||||
|
rotation != VIPS_ANGLE_D0);
|
||||||
// Auto-rotate post-extract
|
// Auto-rotate post-extract
|
||||||
if (autoRotation != VIPS_ANGLE_D0) {
|
if (autoRotation != VIPS_ANGLE_D0) {
|
||||||
image = image.rot(autoRotation);
|
image = image.rot(autoRotation);
|
||||||
@@ -402,7 +413,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
||||||
baton->joinChannelIn[i]->access = baton->input->access;
|
baton->joinChannelIn[i]->access = access;
|
||||||
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
||||||
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
||||||
image = image.bandjoin(joinImage);
|
image = image.bandjoin(joinImage);
|
||||||
@@ -471,10 +482,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
|
|
||||||
// Attention-based or Entropy-based crop
|
// Attention-based or Entropy-based crop
|
||||||
MultiPageUnsupported(nPages, "Resize strategy");
|
MultiPageUnsupported(nPages, "Resize strategy");
|
||||||
image = image.tilecache(VImage::option()
|
image = sharp::StaySequential(image, access);
|
||||||
->set("access", VIPS_ACCESS_RANDOM)
|
|
||||||
->set("threaded", TRUE));
|
|
||||||
|
|
||||||
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
||||||
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
||||||
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
|
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
|
||||||
@@ -495,6 +503,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Rotate post-extract non-90 angle
|
// Rotate post-extract non-90 angle
|
||||||
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
|
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
|
||||||
MultiPageUnsupported(nPages, "Rotate");
|
MultiPageUnsupported(nPages, "Rotate");
|
||||||
|
image = sharp::StaySequential(image, access);
|
||||||
std::vector<double> background;
|
std::vector<double> background;
|
||||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
|
||||||
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
||||||
@@ -518,6 +527,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Affine transform
|
// Affine transform
|
||||||
if (!baton->affineMatrix.empty()) {
|
if (!baton->affineMatrix.empty()) {
|
||||||
MultiPageUnsupported(nPages, "Affine");
|
MultiPageUnsupported(nPages, "Affine");
|
||||||
|
image = sharp::StaySequential(image, access);
|
||||||
std::vector<double> background;
|
std::vector<double> background;
|
||||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
||||||
vips::VInterpolate interp = vips::VInterpolate::new_from_name(
|
vips::VInterpolate interp = vips::VInterpolate::new_from_name(
|
||||||
@@ -614,7 +624,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
for (Composite *composite : baton->composite) {
|
for (Composite *composite : baton->composite) {
|
||||||
VImage compositeImage;
|
VImage compositeImage;
|
||||||
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
||||||
composite->input->access = baton->input->access;
|
composite->input->access = access;
|
||||||
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
||||||
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
|
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
|
||||||
// Verify within current dimensions
|
// Verify within current dimensions
|
||||||
@@ -701,11 +711,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
|
|
||||||
// Apply normalisation - stretch luminance to cover full dynamic range
|
// Apply normalisation - stretch luminance to cover full dynamic range
|
||||||
if (baton->normalise) {
|
if (baton->normalise) {
|
||||||
image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
|
image = sharp::StaySequential(image, access);
|
||||||
|
image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
||||||
if (baton->claheWidth != 0 && baton->claheHeight != 0) {
|
if (baton->claheWidth != 0 && baton->claheHeight != 0) {
|
||||||
|
image = sharp::StaySequential(image, access);
|
||||||
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
|
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,7 +725,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
if (baton->boolean != nullptr) {
|
if (baton->boolean != nullptr) {
|
||||||
VImage booleanImage;
|
VImage booleanImage;
|
||||||
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
||||||
baton->boolean->access = baton->input->access;
|
baton->boolean->access = access;
|
||||||
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
||||||
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
||||||
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
||||||
@@ -776,9 +788,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply output ICC profile
|
// Apply output ICC profile
|
||||||
if (!baton->withMetadataIcc.empty()) {
|
if (baton->withMetadata) {
|
||||||
image = image.icc_transform(
|
image = image.icc_transform(
|
||||||
const_cast<char*>(baton->withMetadataIcc.data()),
|
baton->withMetadataIcc.empty() ? "srgb" : const_cast<char*>(baton->withMetadataIcc.data()),
|
||||||
VImage::option()
|
VImage::option()
|
||||||
->set("input_profile", processingProfile)
|
->set("input_profile", processingProfile)
|
||||||
->set("embedded", TRUE)
|
->set("embedded", TRUE)
|
||||||
@@ -882,6 +894,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("lossless", baton->webpLossless)
|
->set("lossless", baton->webpLossless)
|
||||||
->set("near_lossless", baton->webpNearLossless)
|
->set("near_lossless", baton->webpNearLossless)
|
||||||
->set("smart_subsample", baton->webpSmartSubsample)
|
->set("smart_subsample", baton->webpSmartSubsample)
|
||||||
|
->set("preset", baton->webpPreset)
|
||||||
->set("effort", baton->webpEffort)
|
->set("effort", baton->webpEffort)
|
||||||
->set("min_size", baton->webpMinSize)
|
->set("min_size", baton->webpMinSize)
|
||||||
->set("mixed", baton->webpMixed)
|
->set("mixed", baton->webpMixed)
|
||||||
@@ -963,6 +976,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
if (!sharp::HasAlpha(image)) {
|
if (!sharp::HasAlpha(image)) {
|
||||||
baton->tileBackground.pop_back();
|
baton->tileBackground.pop_back();
|
||||||
}
|
}
|
||||||
|
image = sharp::StaySequential(image, access, baton->tileAngle != 0);
|
||||||
vips::VOption *options = BuildOptionsDZ(baton);
|
vips::VOption *options = BuildOptionsDZ(baton);
|
||||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
@@ -1086,6 +1100,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("lossless", baton->webpLossless)
|
->set("lossless", baton->webpLossless)
|
||||||
->set("near_lossless", baton->webpNearLossless)
|
->set("near_lossless", baton->webpNearLossless)
|
||||||
->set("smart_subsample", baton->webpSmartSubsample)
|
->set("smart_subsample", baton->webpSmartSubsample)
|
||||||
|
->set("preset", baton->webpPreset)
|
||||||
->set("effort", baton->webpEffort)
|
->set("effort", baton->webpEffort)
|
||||||
->set("min_size", baton->webpMinSize)
|
->set("min_size", baton->webpMinSize)
|
||||||
->set("mixed", baton->webpMixed)
|
->set("mixed", baton->webpMixed)
|
||||||
@@ -1162,6 +1177,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
if (!sharp::HasAlpha(image)) {
|
if (!sharp::HasAlpha(image)) {
|
||||||
baton->tileBackground.pop_back();
|
baton->tileBackground.pop_back();
|
||||||
}
|
}
|
||||||
|
image = sharp::StaySequential(image, access, baton->tileAngle != 0);
|
||||||
vips::VOption *options = BuildOptionsDZ(baton);
|
vips::VOption *options = BuildOptionsDZ(baton);
|
||||||
image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
|
image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
|
||||||
baton->formatOut = "dz";
|
baton->formatOut = "dz";
|
||||||
@@ -1363,6 +1379,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
||||||
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
||||||
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
||||||
|
{"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
|
||||||
{"min_size", baton->webpMinSize ? "TRUE" : "FALSE"},
|
{"min_size", baton->webpMinSize ? "TRUE" : "FALSE"},
|
||||||
{"mixed", baton->webpMixed ? "TRUE" : "FALSE"},
|
{"mixed", baton->webpMixed ? "TRUE" : "FALSE"},
|
||||||
{"effort", std::to_string(baton->webpEffort)}
|
{"effort", std::to_string(baton->webpEffort)}
|
||||||
@@ -1615,6 +1632,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
||||||
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
||||||
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
||||||
|
baton->webpPreset = sharp::AttrAsEnum<VipsForeignWebpPreset>(options, "webpPreset", VIPS_TYPE_FOREIGN_WEBP_PRESET);
|
||||||
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
||||||
baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
|
baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
|
||||||
baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
|
baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
|
||||||
@@ -1674,23 +1692,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->tileId = sharp::AttrAsStr(options, "tileId");
|
baton->tileId = sharp::AttrAsStr(options, "tileId");
|
||||||
baton->tileBasename = sharp::AttrAsStr(options, "tileBasename");
|
baton->tileBasename = sharp::AttrAsStr(options, "tileBasename");
|
||||||
|
|
||||||
// Force random access for certain operations
|
|
||||||
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
|
|
||||||
if (
|
|
||||||
baton->trimThreshold > 0.0 ||
|
|
||||||
baton->normalise ||
|
|
||||||
baton->position == 16 || baton->position == 17 ||
|
|
||||||
baton->angle != 0 ||
|
|
||||||
baton->rotationAngle != 0.0 ||
|
|
||||||
baton->tileAngle != 0 ||
|
|
||||||
baton->flip ||
|
|
||||||
baton->claheWidth != 0 ||
|
|
||||||
!baton->affineMatrix.empty()
|
|
||||||
) {
|
|
||||||
baton->input->access = VIPS_ACCESS_RANDOM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to notify of libvips warnings
|
// Function to notify of libvips warnings
|
||||||
Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
|
Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ struct PipelineBaton {
|
|||||||
bool webpNearLossless;
|
bool webpNearLossless;
|
||||||
bool webpLossless;
|
bool webpLossless;
|
||||||
bool webpSmartSubsample;
|
bool webpSmartSubsample;
|
||||||
|
VipsForeignWebpPreset webpPreset;
|
||||||
int webpEffort;
|
int webpEffort;
|
||||||
bool webpMinSize;
|
bool webpMinSize;
|
||||||
bool webpMixed;
|
bool webpMixed;
|
||||||
@@ -318,6 +319,7 @@ struct PipelineBaton {
|
|||||||
webpNearLossless(false),
|
webpNearLossless(false),
|
||||||
webpLossless(false),
|
webpLossless(false),
|
||||||
webpSmartSubsample(false),
|
webpSmartSubsample(false),
|
||||||
|
webpPreset(VIPS_FOREIGN_WEBP_PRESET_DEFAULT),
|
||||||
webpEffort(4),
|
webpEffort(4),
|
||||||
webpMinSize(false),
|
webpMinSize(false),
|
||||||
webpMixed(false),
|
webpMixed(false),
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|||||||
exports.Set("simd", Napi::Function::New(env, simd));
|
exports.Set("simd", Napi::Function::New(env, simd));
|
||||||
exports.Set("libvipsVersion", Napi::Function::New(env, libvipsVersion));
|
exports.Set("libvipsVersion", Napi::Function::New(env, libvipsVersion));
|
||||||
exports.Set("format", Napi::Function::New(env, format));
|
exports.Set("format", Napi::Function::New(env, format));
|
||||||
|
exports.Set("block", Napi::Function::New(env, block));
|
||||||
exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
|
exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
|
||||||
exports.Set("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
|
exports.Set("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
|
||||||
exports.Set("stats", Napi::Function::New(env, stats));
|
exports.Set("stats", Napi::Function::New(env, stats));
|
||||||
|
|||||||
@@ -164,6 +164,17 @@ Napi::Value format(const Napi::CallbackInfo& info) {
|
|||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
(Un)block libvips operations at runtime.
|
||||||
|
*/
|
||||||
|
void block(const Napi::CallbackInfo& info) {
|
||||||
|
Napi::Array ops = info[size_t(0)].As<Napi::Array>();
|
||||||
|
bool const state = info[size_t(1)].As<Napi::Boolean>().Value();
|
||||||
|
for (unsigned int i = 0; i < ops.Length(); i++) {
|
||||||
|
vips_operation_block_set(ops.Get(i).As<Napi::String>().Utf8Value().c_str(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Synchronous, internal-only method used by some of the functional tests.
|
Synchronous, internal-only method used by some of the functional tests.
|
||||||
Calculates the maximum colour distance using the DE2000 algorithm
|
Calculates the maximum colour distance using the DE2000 algorithm
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Napi::Value counters(const Napi::CallbackInfo& info);
|
|||||||
Napi::Value simd(const Napi::CallbackInfo& info);
|
Napi::Value simd(const Napi::CallbackInfo& info);
|
||||||
Napi::Value libvipsVersion(const Napi::CallbackInfo& info);
|
Napi::Value libvipsVersion(const Napi::CallbackInfo& info);
|
||||||
Napi::Value format(const Napi::CallbackInfo& info);
|
Napi::Value format(const Napi::CallbackInfo& info);
|
||||||
|
void block(const Napi::CallbackInfo& info);
|
||||||
Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
|
Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
|
||||||
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info);
|
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ARG BRANCH=main
|
|||||||
RUN apt-get -y update && apt-get install -y build-essential curl git
|
RUN apt-get -y update && apt-get install -y build-essential curl git
|
||||||
|
|
||||||
# Install latest Node.js LTS
|
# Install latest Node.js LTS
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
|
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
|
||||||
RUN apt-get install -y nodejs
|
RUN apt-get install -y nodejs
|
||||||
|
|
||||||
# Install benchmark dependencies
|
# Install benchmark dependencies
|
||||||
@@ -23,4 +23,9 @@ RUN cat /etc/os-release | grep VERSION=
|
|||||||
RUN node -v
|
RUN node -v
|
||||||
|
|
||||||
WORKDIR /tmp/sharp/test/bench
|
WORKDIR /tmp/sharp/test/bench
|
||||||
|
|
||||||
|
# Workaround for: https://github.com/emscripten-core/emscripten/pull/16917
|
||||||
|
# This could be removed once Squoosh is an optional dependency.
|
||||||
|
ENV NODE_OPTIONS="--no-experimental-fetch"
|
||||||
|
|
||||||
CMD [ "node", "perf" ]
|
CMD [ "node", "perf" ]
|
||||||
|
|||||||
@@ -9,15 +9,15 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@squoosh/cli": "0.7.3",
|
"@squoosh/cli": "0.7.3",
|
||||||
"@squoosh/lib": "0.4.0",
|
"@squoosh/lib": "0.5.3",
|
||||||
"async": "3.2.4",
|
"async": "3.2.4",
|
||||||
"benchmark": "2.1.4",
|
"benchmark": "2.1.4",
|
||||||
"gm": "1.25.0",
|
"gm": "1.25.0",
|
||||||
"imagemagick": "0.1.3",
|
"imagemagick": "0.1.3",
|
||||||
"jimp": "0.22.7"
|
"jimp": "0.22.10"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tensorflow/tfjs-node": "4.2.0",
|
"@tensorflow/tfjs-node": "4.9.0",
|
||||||
"mapnik": "4.5.9"
|
"mapnik": "4.5.9"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ async.series({
|
|||||||
jpegSuite.add('squoosh-lib-buffer-buffer', {
|
jpegSuite.add('squoosh-lib-buffer-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
const pool = new squoosh.ImagePool();
|
const pool = new squoosh.ImagePool(os.cpus().length);
|
||||||
const image = pool.ingestImage(inputJpgBuffer);
|
const image = pool.ingestImage(inputJpgBuffer);
|
||||||
image.decoded
|
image.decoded
|
||||||
.then(function () {
|
.then(function () {
|
||||||
@@ -652,7 +652,7 @@ async.series({
|
|||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
image
|
image
|
||||||
.resize(width, heightPng)
|
.resize(width, heightPng, jimp.RESIZE_BICUBIC)
|
||||||
.deflateLevel(6)
|
.deflateLevel(6)
|
||||||
.filterType(0)
|
.filterType(0)
|
||||||
.getBuffer(jimp.MIME_PNG, function (err) {
|
.getBuffer(jimp.MIME_PNG, function (err) {
|
||||||
@@ -673,7 +673,7 @@ async.series({
|
|||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
image
|
image
|
||||||
.resize(width, heightPng)
|
.resize(width, heightPng, jimp.RESIZE_BICUBIC)
|
||||||
.deflateLevel(6)
|
.deflateLevel(6)
|
||||||
.filterType(0)
|
.filterType(0)
|
||||||
.write(outputPng, function (err) {
|
.write(outputPng, function (err) {
|
||||||
@@ -707,7 +707,7 @@ async.series({
|
|||||||
pngSuite.add('squoosh-lib-buffer-buffer', {
|
pngSuite.add('squoosh-lib-buffer-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
const pool = new squoosh.ImagePool();
|
const pool = new squoosh.ImagePool(os.cpus().length);
|
||||||
const image = pool.ingestImage(inputPngBuffer);
|
const image = pool.ingestImage(inputPngBuffer);
|
||||||
image.decoded
|
image.decoded
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
|||||||
@@ -524,7 +524,7 @@ sharp('input.tiff').jxl({ lossless: true }).toFile('out.jxl');
|
|||||||
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
|
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
|
||||||
|
|
||||||
// Support `minSize` and `mixed` webp options
|
// Support `minSize` and `mixed` webp options
|
||||||
sharp('input.tiff').webp({ minSize: 1000, mixed: true }).toFile('out.gif');
|
sharp('input.tiff').webp({ minSize: true, mixed: true }).toFile('out.gif');
|
||||||
|
|
||||||
// 'failOn' input param
|
// 'failOn' input param
|
||||||
sharp('input.tiff', { failOn: 'none' });
|
sharp('input.tiff', { failOn: 'none' });
|
||||||
@@ -651,3 +651,13 @@ sharp(input).composite([
|
|||||||
unlimited: true,
|
unlimited: true,
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Support for webp preset in types
|
||||||
|
// https://github.com/lovell/sharp/issues/3747
|
||||||
|
sharp('input.tiff').webp({ preset: 'photo' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'picture' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'icon' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'drawing' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'text' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'default' }).toFile('out.webp');
|
||||||
|
|
||||||
|
|||||||
@@ -781,6 +781,19 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('withMetadata adds default sRGB profile', async () => {
|
||||||
|
const data = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(32, 24)
|
||||||
|
.withMetadata()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
const { colorSpace, deviceClass, intent } = icc.parse(metadata.icc);
|
||||||
|
assert.strictEqual(colorSpace, 'RGB');
|
||||||
|
assert.strictEqual(deviceClass, 'Monitor');
|
||||||
|
assert.strictEqual(intent, 'Perceptual');
|
||||||
|
});
|
||||||
|
|
||||||
it('File input with corrupt header fails gracefully', function (done) {
|
it('File input with corrupt header fails gracefully', function (done) {
|
||||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||||
.metadata(function (err) {
|
.metadata(function (err) {
|
||||||
|
|||||||
@@ -192,6 +192,23 @@ describe('Rotation', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Auto-rotate by 270 degrees, rectangular output ignoring aspect ratio', function (done) {
|
||||||
|
sharp(fixtures.inputJpgWithLandscapeExif8)
|
||||||
|
.resize(320, 240, { fit: sharp.fit.fill })
|
||||||
|
.rotate()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
sharp(data).metadata(function (err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, metadata.width);
|
||||||
|
assert.strictEqual(240, metadata.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Rotate by 30 degrees, rectangular output ignoring aspect ratio', function (done) {
|
it('Rotate by 30 degrees, rectangular output ignoring aspect ratio', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(320, 240, { fit: sharp.fit.fill })
|
.resize(320, 240, { fit: sharp.fit.fill })
|
||||||
@@ -489,4 +506,28 @@ describe('Rotation', function () {
|
|||||||
.timeout({ seconds: 5 })
|
.timeout({ seconds: 5 })
|
||||||
.toBuffer()
|
.toBuffer()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it('Rotate 90 then resize with inside fit', async () => {
|
||||||
|
const data = await sharp({ create: { width: 16, height: 8, channels: 3, background: 'red' } })
|
||||||
|
.rotate(90)
|
||||||
|
.resize({ width: 6, fit: 'inside' })
|
||||||
|
.png({ compressionLevel: 0 })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { width, height } = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(width, 6);
|
||||||
|
assert.strictEqual(height, 12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Resize with inside fit then rotate 90', async () => {
|
||||||
|
const data = await sharp({ create: { width: 16, height: 8, channels: 3, background: 'red' } })
|
||||||
|
.resize({ width: 6, fit: 'inside' })
|
||||||
|
.rotate(90)
|
||||||
|
.png({ compressionLevel: 0 })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { width, height } = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(width, 3);
|
||||||
|
assert.strictEqual(height, 6);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -928,7 +928,7 @@ describe('Tile', function () {
|
|||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, stat.isFile());
|
assert.strictEqual(true, stat.isFile());
|
||||||
assert.strictEqual(true, stat.size > 0);
|
assert.strictEqual(true, stat.size > 0);
|
||||||
extractZip(container, { dir: path.dirname(extractTo) })
|
extractZip(container, { dir: extractTo })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
assertDeepZoomTiles(directory, 256, 13, done);
|
assertDeepZoomTiles(directory, 256, 13, done);
|
||||||
})
|
})
|
||||||
@@ -959,7 +959,7 @@ describe('Tile', function () {
|
|||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, stat.isFile());
|
assert.strictEqual(true, stat.isFile());
|
||||||
assert.strictEqual(true, stat.size > 0);
|
assert.strictEqual(true, stat.size > 0);
|
||||||
extractZip(container, { dir: path.dirname(extractTo) })
|
extractZip(container, { dir: extractTo })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
assertDeepZoomTiles(directory, 256, 13, done);
|
assertDeepZoomTiles(directory, 256, 13, done);
|
||||||
})
|
})
|
||||||
@@ -988,7 +988,7 @@ describe('Tile', function () {
|
|||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, stat.isFile());
|
assert.strictEqual(true, stat.isFile());
|
||||||
assert.strictEqual(true, stat.size > 0);
|
assert.strictEqual(true, stat.size > 0);
|
||||||
extractZip(container, { dir: path.dirname(extractTo) })
|
extractZip(container, { dir: extractTo })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
assertDeepZoomTiles(directory, 256, 13, done);
|
assertDeepZoomTiles(directory, 256, 13, done);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -153,4 +153,41 @@ describe('Utilities', function () {
|
|||||||
assert.strictEqual(true, Array.isArray(sharp.vendor.installed));
|
assert.strictEqual(true, Array.isArray(sharp.vendor.installed));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Block', () => {
|
||||||
|
it('Can block a named operation', () => {
|
||||||
|
sharp.block({ operation: ['test'] });
|
||||||
|
});
|
||||||
|
it('Can unblock a named operation', () => {
|
||||||
|
sharp.unblock({ operation: ['test'] });
|
||||||
|
});
|
||||||
|
it('Invalid block operation throws', () => {
|
||||||
|
assert.throws(() => sharp.block(1),
|
||||||
|
/Expected object for options but received 1 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(() => sharp.block({}),
|
||||||
|
/Expected Array<string> for operation but received undefined of type undefined/
|
||||||
|
);
|
||||||
|
assert.throws(() => sharp.block({ operation: 'fail' }),
|
||||||
|
/Expected Array<string> for operation but received fail of type string/
|
||||||
|
);
|
||||||
|
assert.throws(() => sharp.block({ operation: ['maybe', false] }),
|
||||||
|
/Expected Array<string> for operation but received maybe,false of type object/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Invalid unblock operation throws', () => {
|
||||||
|
assert.throws(() => sharp.unblock(1),
|
||||||
|
/Expected object for options but received 1 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(() => sharp.unblock({}),
|
||||||
|
/Expected Array<string> for operation but received undefined of type undefined/
|
||||||
|
);
|
||||||
|
assert.throws(() => sharp.unblock({ operation: 'fail' }),
|
||||||
|
/Expected Array<string> for operation but received fail of type string/
|
||||||
|
);
|
||||||
|
assert.throws(() => sharp.unblock({ operation: ['maybe', false] }),
|
||||||
|
/Expected Array<string> for operation but received maybe,false of type object/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -102,6 +102,29 @@ describe('WebP', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should produce a different file size with specific preset', () =>
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.webp({ preset: 'default' })
|
||||||
|
.toBuffer()
|
||||||
|
.then(presetDefault =>
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.webp({ preset: 'picture' })
|
||||||
|
.toBuffer()
|
||||||
|
.then(presetPicture => {
|
||||||
|
assert.notStrictEqual(presetDefault.length, presetPicture.length);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
it('invalid preset throws', () => {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp().webp({ preset: 'fail' }),
|
||||||
|
/Expected one of: default, photo, picture, drawing, icon, text for preset but received fail of type string/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should produce a smaller file size with increased effort', () =>
|
it('should produce a smaller file size with increased effort', () =>
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
|
|||||||
Reference in New Issue
Block a user