mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 05:36: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:
|
||||
image_family: freebsd-14-0-snap
|
||||
image_family: freebsd-13-2
|
||||
|
||||
task:
|
||||
name: FreeBSD
|
||||
@@ -9,7 +9,7 @@ task:
|
||||
prerequisites_script:
|
||||
- pkg update -f
|
||||
- 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:
|
||||
- npm install --build-from-source --unsafe-perm
|
||||
test_script:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@ node_modules
|
||||
/coverage
|
||||
test/bench/node_modules
|
||||
test/fixtures/output*
|
||||
test/fixtures/vips-properties.xml
|
||||
test/leak/libvips.supp
|
||||
test/saliency/report.json
|
||||
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.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.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[].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[].top] | <code>Number</code> | | the pixel offset from the top 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[].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[].raw] | <code>Object</code> | | describes overlay when using raw pixel data. |
|
||||
| [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. |
|
||||
| [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.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. |
|
||||
@@ -84,7 +84,7 @@ readableStream.pipe(transformer).pipe(writableStream);
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
// Create a blank 300x200 PNG image of semi-transluent red pixels
|
||||
// Create a blank 300x200 PNG image of semi-translucent red pixels
|
||||
sharp({
|
||||
create: {
|
||||
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
|
||||
- `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.
|
||||
- `paletteBitDepth`: Bit depth of palette-based image (GIF, PNG).
|
||||
- `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.
|
||||
- `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.
|
||||
|
||||
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,
|
||||
the background colour can be provided with the `background` option.
|
||||
@@ -533,7 +533,7 @@ await sharp(rgbInput)
|
||||
|
||||
|
||||
## recomb
|
||||
Recomb the image with the specified matrix.
|
||||
Recombine the image with the specified matrix.
|
||||
|
||||
|
||||
**Throws**:
|
||||
@@ -556,7 +556,7 @@ sharp(input)
|
||||
])
|
||||
.raw()
|
||||
.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
|
||||
});
|
||||
```
|
||||
@@ -607,7 +607,7 @@ const output = await sharp(input)
|
||||
```
|
||||
**Example**
|
||||
```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)
|
||||
.modulate({
|
||||
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.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.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.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) |
|
||||
|
||||
@@ -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.
|
||||
|
||||
<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:
|
||||
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
||||
|
||||
@@ -166,3 +166,58 @@ const simd = sharp.simd();
|
||||
const simd = sharp.simd(false);
|
||||
// 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*
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 {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 {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 {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 {Number} [images[].top] - the pixel offset from the top 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[].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 {Object} [images[].raw] - describes overlay when using raw pixel data.
|
||||
* @param {Number} [images[].raw.width]
|
||||
|
||||
@@ -49,7 +49,7 @@ const debuglog = util.debuglog('sharp');
|
||||
* readableStream.pipe(transformer).pipe(writableStream);
|
||||
*
|
||||
* @example
|
||||
* // Create a blank 300x200 PNG image of semi-transluent red pixels
|
||||
* // Create a blank 300x200 PNG image of semi-translucent red pixels
|
||||
* sharp({
|
||||
* create: {
|
||||
* 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.
|
||||
* 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 {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
|
||||
* (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).
|
||||
@@ -291,6 +291,7 @@ const Sharp = function (input, options) {
|
||||
webpLossless: false,
|
||||
webpNearLossless: false,
|
||||
webpSmartSubsample: false,
|
||||
webpPreset: 'default',
|
||||
webpEffort: 4,
|
||||
webpMinSize: 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) */
|
||||
effort?: number | undefined;
|
||||
/** 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) */
|
||||
mixed?: boolean;
|
||||
/** Preset options: one of default, photo, picture, drawing, icon, text (optional, default 'default') */
|
||||
preset?: keyof PresetEnum | undefined;
|
||||
}
|
||||
|
||||
interface AvifOptions extends OutputOptions {
|
||||
@@ -1476,6 +1478,15 @@ declare namespace sharp {
|
||||
lanczos3: 'lanczos3';
|
||||
}
|
||||
|
||||
interface PresetEnum {
|
||||
default: 'default';
|
||||
picture: 'picture';
|
||||
photo: 'photo';
|
||||
drawing: 'drawing';
|
||||
icon: 'icon';
|
||||
text: 'text';
|
||||
}
|
||||
|
||||
interface BoolEnum {
|
||||
and: 'and';
|
||||
or: 'or';
|
||||
|
||||
@@ -432,6 +432,7 @@ function _isStreamInput () {
|
||||
* - `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
|
||||
* - `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.
|
||||
* - `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
|
||||
|
||||
@@ -11,7 +11,7 @@ const is = require('./is');
|
||||
* or auto-orient based on the EXIF `Orientation` tag.
|
||||
*
|
||||
* 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,
|
||||
* 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
|
||||
*
|
||||
@@ -785,7 +785,7 @@ function linear (a, b) {
|
||||
* ])
|
||||
* .raw()
|
||||
* .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
|
||||
* });
|
||||
*
|
||||
@@ -842,7 +842,7 @@ function recomb (inputMatrix) {
|
||||
* .toBuffer();
|
||||
*
|
||||
* @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)
|
||||
* .modulate({
|
||||
* brightness: 0.5,
|
||||
|
||||
@@ -483,6 +483,7 @@ function png (options) {
|
||||
* @param {boolean} [options.lossless=false] - use lossless compression mode
|
||||
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
|
||||
* @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.loop=0] - number of animation iterations, use 0 for infinite animation
|
||||
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
||||
@@ -517,6 +518,13 @@ function webp (options) {
|
||||
if (is.defined(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.integer(options.effort) && is.inRange(options.effort, 0, 6)) {
|
||||
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.
|
||||
*
|
||||
* <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:
|
||||
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
||||
|
||||
@@ -202,6 +202,72 @@ function simd (simd) {
|
||||
}
|
||||
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.
|
||||
* @private
|
||||
@@ -216,4 +282,6 @@ module.exports = function (Sharp) {
|
||||
Sharp.versions = versions;
|
||||
Sharp.vendor = vendor;
|
||||
Sharp.queue = queue;
|
||||
Sharp.block = block;
|
||||
Sharp.unblock = unblock;
|
||||
};
|
||||
|
||||
28
package.json
28
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"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>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -133,7 +133,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.1",
|
||||
"detect-libc": "^2.0.2",
|
||||
"node-addon-api": "^6.1.0",
|
||||
"prebuild-install": "^7.1.1",
|
||||
"semver": "^7.5.4",
|
||||
@@ -159,19 +159,19 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"config": {
|
||||
"libvips": "8.14.2",
|
||||
"libvips": "8.14.4",
|
||||
"integrity": {
|
||||
"darwin-arm64v8": "sha512-eUuxg6H0tXgX4z2lsaGtZ4cbPAm7yoFgkvPDd4csxoiVt+QUB25pEJwiXw7oB53VlBFIp3O8lbydSFS5zH8MQQ==",
|
||||
"darwin-x64": "sha512-cMT4v76IgzSR0VoXqLk/yftRyzMEZ+SBVMLzXCgqP/lmnYisrpmHHNqrWnoZbUUBXbPXLn6KMultYOJHe/c9ZQ==",
|
||||
"linux-arm64v8": "sha512-OcDJ/ly80pxwaKnw0W91sSvZczPtWsjmzrY/+6NMiQZT84LkmeaRuwErbHhorKDxnl7iZuNn9Uj5V25Xmj+LDQ==",
|
||||
"linux-armv6": "sha512-hk2ohSOYTJEtVQxEQFyQ+tuayKpYqx6NiXa7AE+8MF+yscxt+g+mLJ7TjDqtmb4ttFGH4IVfsEfU2YXIqWqkpg==",
|
||||
"linux-armv7": "sha512-/5Ci2Cd+yLZmTaEt9lVJ89elxX3RMJpps0ESjj43X40yrwka51QfXeg1QV38uNzZpCDIZkrbXZK0lyKldjpLuA==",
|
||||
"linux-x64": "sha512-wjCKmWfBb0uz1UB7rPDLvO0s+VWuoAY/Vv/YGCRFEQUkdSLQUgHExrOMMbOM3FleuYfQqznDYCXXphkl7X44+w==",
|
||||
"linuxmusl-arm64v8": "sha512-QtD2n90yi+rLE65C0gksFUU5uMUFPICI/pS3A0bgthpIcoCejAOYs3ZjVWpZbHQuV/lWahIUYO78MB9CzY860A==",
|
||||
"linuxmusl-x64": "sha512-TokQ/ETCJAsPYuxIMOPYDp25rlcwtpmIMtRUR9PB75TmZEJe7abRfCEInIPYeD8F/HxxnJSLiEdlbn1z1Jfzng==",
|
||||
"win32-arm64v8": "sha512-IIuj4EAgLqEVAoOuYH79C61a7TcJXlU/RBwk+5JsGWc2mr4J/Ar5J01e6XBvU4Lu3eqcU+3GPaACZEa1511buA==",
|
||||
"win32-ia32": "sha512-CsZi7lrReX3B6tmYgOGJ0IiAfcN5APDC6l+3gdosxfTfwpLLO+jXaSmyNwIGeMqrdgckG/gwwc+IrUZmkmjJ/A==",
|
||||
"win32-x64": "sha512-J7znmNKUK4ZKo6SnSnEtzT1xRAwvkGXxIx9/QihAadu1TFdS06yNhcENmwC4973+KZBlAdVpWbZ8sLrEoWkdCA=="
|
||||
"darwin-arm64v8": "sha512-jZt5+ZBQzdloop9z/XlOAy8jHxD+ZGt3J8YUm1g3njjjKmZ/RmM9r6QAeLLILe67ATHaaAtmCil37fDc400OrQ==",
|
||||
"darwin-x64": "sha512-Mhpr8n8CjrU+u5K9YLucmkCgwtJGexECLOZejPfqM8CiOMerowR0wJTuSt9WTOtb9qGOL/ndybfrymsw+YH8PA==",
|
||||
"linux-arm64v8": "sha512-k2PiOOv8amzS4m5jc4Vceozv8h041IoyHL/1s0Rj290jg3w6BUJL3V+TLwKUPM35i7rV5rm14gtnGZ7qKENdmA==",
|
||||
"linux-armv6": "sha512-CuPTo50owR8P+BCCcWk1tF4qB3XSAaHeaIzSanJM/v9zBZfUfMGI0OLv+ByyHCL3BE2CbGXaSXhuEVw2JQ08Sw==",
|
||||
"linux-armv7": "sha512-CvD6fMy9PkZk1m2UPTWDcFfcD4qFA3RALyAWIih8ftOY9ksI3Y4uz6c0ML+ixBl0hqQK3WEg6+ac5TGDjZbbYA==",
|
||||
"linux-x64": "sha512-vqoV61ka2hBYQ5582nQlyUcVPtItu927mng9RUU9nyO4Wt50z9nNT/pfcYEfF2jkBNW9JaiMaj6bENHgxA6mMg==",
|
||||
"linuxmusl-arm64v8": "sha512-iCyl0y/qxdvgGidsYn11R8d4TEcU92uYHtYI8FSHyUobZw/9i2y3189PUTQ/fw44oqaBzTR3p9NF2eP6aLT9gQ==",
|
||||
"linuxmusl-x64": "sha512-qj7IUqWUqCtxECpgNp4E1NcIbsNe1ujzBuJcnnQot7GZOuPUhI5N6ZUhozmh6LfbGFdBZpPc/JFh1eDZ0IEpbQ==",
|
||||
"win32-arm64v8": "sha512-VRi7fpE9Kb3xQGcNmPPTJnWGAEUMq+YOq9abpaIIB2r3Ax327/7wHS7o2ezD6zQKdxIX6gODC5io/hReIJ9Jnw==",
|
||||
"win32-ia32": "sha512-EnvtU7Q6+pjl5/Y1/UngCFDM2CSqpYWVDwY03ilUKSuqTeDKTJYyus0rJ+n6p4nmdjJlVdhYlkvpy8kkEAtDHg==",
|
||||
"win32-x64": "sha512-fCl/KQuSijVYC8hULWbff8Mfuh3vjjdz4j5p73VgdLP6aZUrHctbhBvEIe0aQ8HpmcGdBnATX5pXUQ4GDl3mwQ=="
|
||||
},
|
||||
"runtime": "napi",
|
||||
"target": 7
|
||||
|
||||
@@ -964,12 +964,7 @@ namespace sharp {
|
||||
}
|
||||
|
||||
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
||||
Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction) {
|
||||
if (swap && canvas != Canvas::IGNORE_ASPECT) {
|
||||
// Swap input width and height when requested.
|
||||
std::swap(width, height);
|
||||
}
|
||||
|
||||
Canvas canvas, bool withoutEnlargement, bool withoutReduction) {
|
||||
double hshrink = 1.0;
|
||||
double vshrink = 1.0;
|
||||
|
||||
@@ -1035,4 +1030,13 @@ namespace sharp {
|
||||
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
|
||||
|
||||
16
src/common.h
16
src/common.h
@@ -15,8 +15,8 @@
|
||||
|
||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 14) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 2)
|
||||
#error "libvips version 8.14.2+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 4)
|
||||
#error "libvips version 8.14.4+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
#endif
|
||||
|
||||
#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);
|
||||
|
||||
/*
|
||||
Calculate the shrink factor, taking into account auto-rotate, the canvas
|
||||
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.
|
||||
Calculate the horizontal and vertical shrink factors, taking the canvas mode into account.
|
||||
*/
|
||||
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
|
||||
|
||||
|
||||
@@ -20,18 +20,15 @@
|
||||
#include "operations.h"
|
||||
#include "pipeline.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#ifdef _WIN32
|
||||
#define STAT64_STRUCT __stat64
|
||||
#define STAT64_FUNCTION _stat64
|
||||
#elif defined(__APPLE__)
|
||||
#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
|
||||
#elif defined(_LARGEFILE64_SOURCE)
|
||||
#define STAT64_STRUCT stat64
|
||||
#define STAT64_FUNCTION stat64
|
||||
#else
|
||||
#define STAT64_STRUCT stat
|
||||
#define STAT64_FUNCTION stat
|
||||
#endif
|
||||
|
||||
class PipelineWorker : public Napi::AsyncWorker {
|
||||
@@ -56,6 +53,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
vips::VImage image;
|
||||
sharp::ImageType inputImageType;
|
||||
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
||||
VipsAccess access = baton->input->access;
|
||||
image = sharp::EnsureColourspace(image, baton->colourspaceInput);
|
||||
|
||||
int nPages = baton->input->pages;
|
||||
@@ -79,9 +77,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Rotate and flip image according to Exif orientation
|
||||
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
||||
image = sharp::RemoveExifOrientation(image);
|
||||
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL && (autoRotation != VIPS_ANGLE_D0 || autoFlip)) {
|
||||
image = image.copy_memory();
|
||||
}
|
||||
} else {
|
||||
rotation = CalculateAngleRotation(baton->angle);
|
||||
}
|
||||
@@ -93,6 +88,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
baton->rotationAngle != 0.0);
|
||||
|
||||
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) {
|
||||
image = image.rot(autoRotation);
|
||||
autoRotation = VIPS_ANGLE_D0;
|
||||
@@ -126,6 +128,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Trim
|
||||
if (baton->trimThreshold > 0.0) {
|
||||
MultiPageUnsupported(nPages, "Trim");
|
||||
image = sharp::StaySequential(image, access);
|
||||
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold);
|
||||
baton->trimOffsetLeft = image.xoffset();
|
||||
baton->trimOffsetTop = image.yoffset();
|
||||
@@ -154,15 +157,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
int targetResizeWidth = baton->width;
|
||||
int targetResizeHeight = baton->height;
|
||||
|
||||
// Swap input output width and height when rotating by 90 or 270 degrees
|
||||
bool swap = !baton->rotateBeforePreExtract &&
|
||||
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270 ||
|
||||
autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270);
|
||||
// When auto-rotating by 90 or 270 degrees, swap the target width and
|
||||
// height to ensure the behavior aligns with how it would have been if
|
||||
// the rotation had taken place *before* resizing.
|
||||
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
|
||||
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
||||
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
||||
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
||||
baton->canvas, baton->withoutEnlargement, baton->withoutReduction);
|
||||
|
||||
// The jpeg preload shrink.
|
||||
int jpegShrinkOnLoad = 1;
|
||||
@@ -212,7 +218,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// pdfload* and svgload*
|
||||
if (jpegShrinkOnLoad > 1) {
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", baton->input->access)
|
||||
->set("access", access)
|
||||
->set("shrink", jpegShrinkOnLoad)
|
||||
->set("unlimited", baton->input->unlimited)
|
||||
->set("fail_on", baton->input->failOn);
|
||||
@@ -227,7 +233,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
} else if (scale != 1.0) {
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", baton->input->access)
|
||||
->set("access", access)
|
||||
->set("scale", scale)
|
||||
->set("fail_on", baton->input->failOn);
|
||||
if (inputImageType == sharp::ImageType::WEBP) {
|
||||
@@ -296,7 +302,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Shrink to pageHeight, so we work for multi-page images
|
||||
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
||||
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 targetPageHeight = targetHeight;
|
||||
@@ -379,6 +385,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("kernel", baton->kernel));
|
||||
}
|
||||
|
||||
image = sharp::StaySequential(image, access,
|
||||
autoRotation != VIPS_ANGLE_D0 ||
|
||||
baton->flip ||
|
||||
autoFlip ||
|
||||
rotation != VIPS_ANGLE_D0);
|
||||
// Auto-rotate post-extract
|
||||
if (autoRotation != VIPS_ANGLE_D0) {
|
||||
image = image.rot(autoRotation);
|
||||
@@ -402,7 +413,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
|
||||
|
||||
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]);
|
||||
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
||||
image = image.bandjoin(joinImage);
|
||||
@@ -471,10 +482,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
// Attention-based or Entropy-based crop
|
||||
MultiPageUnsupported(nPages, "Resize strategy");
|
||||
image = image.tilecache(VImage::option()
|
||||
->set("access", VIPS_ACCESS_RANDOM)
|
||||
->set("threaded", TRUE));
|
||||
|
||||
image = sharp::StaySequential(image, access);
|
||||
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
||||
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
||||
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
|
||||
@@ -495,6 +503,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Rotate post-extract non-90 angle
|
||||
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
|
||||
MultiPageUnsupported(nPages, "Rotate");
|
||||
image = sharp::StaySequential(image, access);
|
||||
std::vector<double> background;
|
||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
|
||||
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
||||
@@ -518,6 +527,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Affine transform
|
||||
if (!baton->affineMatrix.empty()) {
|
||||
MultiPageUnsupported(nPages, "Affine");
|
||||
image = sharp::StaySequential(image, access);
|
||||
std::vector<double> background;
|
||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
||||
vips::VInterpolate interp = vips::VInterpolate::new_from_name(
|
||||
@@ -614,7 +624,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
for (Composite *composite : baton->composite) {
|
||||
VImage compositeImage;
|
||||
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
||||
composite->input->access = baton->input->access;
|
||||
composite->input->access = access;
|
||||
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
||||
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
|
||||
// Verify within current dimensions
|
||||
@@ -701,11 +711,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
// Apply normalisation - stretch luminance to cover full dynamic range
|
||||
if (baton->normalise) {
|
||||
image = sharp::StaySequential(image, access);
|
||||
image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
|
||||
}
|
||||
|
||||
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
||||
if (baton->claheWidth != 0 && baton->claheHeight != 0) {
|
||||
image = sharp::StaySequential(image, access);
|
||||
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
|
||||
}
|
||||
|
||||
@@ -713,7 +725,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (baton->boolean != nullptr) {
|
||||
VImage booleanImage;
|
||||
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
||||
baton->boolean->access = baton->input->access;
|
||||
baton->boolean->access = access;
|
||||
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
||||
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
||||
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
||||
@@ -776,9 +788,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
|
||||
// Apply output ICC profile
|
||||
if (!baton->withMetadataIcc.empty()) {
|
||||
if (baton->withMetadata) {
|
||||
image = image.icc_transform(
|
||||
const_cast<char*>(baton->withMetadataIcc.data()),
|
||||
baton->withMetadataIcc.empty() ? "srgb" : const_cast<char*>(baton->withMetadataIcc.data()),
|
||||
VImage::option()
|
||||
->set("input_profile", processingProfile)
|
||||
->set("embedded", TRUE)
|
||||
@@ -882,6 +894,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("lossless", baton->webpLossless)
|
||||
->set("near_lossless", baton->webpNearLossless)
|
||||
->set("smart_subsample", baton->webpSmartSubsample)
|
||||
->set("preset", baton->webpPreset)
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("min_size", baton->webpMinSize)
|
||||
->set("mixed", baton->webpMixed)
|
||||
@@ -963,6 +976,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (!sharp::HasAlpha(image)) {
|
||||
baton->tileBackground.pop_back();
|
||||
}
|
||||
image = sharp::StaySequential(image, access, baton->tileAngle != 0);
|
||||
vips::VOption *options = BuildOptionsDZ(baton);
|
||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
@@ -1086,6 +1100,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("lossless", baton->webpLossless)
|
||||
->set("near_lossless", baton->webpNearLossless)
|
||||
->set("smart_subsample", baton->webpSmartSubsample)
|
||||
->set("preset", baton->webpPreset)
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("min_size", baton->webpMinSize)
|
||||
->set("mixed", baton->webpMixed)
|
||||
@@ -1162,6 +1177,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (!sharp::HasAlpha(image)) {
|
||||
baton->tileBackground.pop_back();
|
||||
}
|
||||
image = sharp::StaySequential(image, access, baton->tileAngle != 0);
|
||||
vips::VOption *options = BuildOptionsDZ(baton);
|
||||
image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
|
||||
baton->formatOut = "dz";
|
||||
@@ -1363,6 +1379,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
||||
{"near_lossless", baton->webpNearLossless ? "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"},
|
||||
{"mixed", baton->webpMixed ? "TRUE" : "FALSE"},
|
||||
{"effort", std::to_string(baton->webpEffort)}
|
||||
@@ -1615,6 +1632,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
||||
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
||||
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->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
|
||||
baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
|
||||
@@ -1674,23 +1692,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->tileId = sharp::AttrAsStr(options, "tileId");
|
||||
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
|
||||
Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
|
||||
|
||||
|
||||
@@ -153,6 +153,7 @@ struct PipelineBaton {
|
||||
bool webpNearLossless;
|
||||
bool webpLossless;
|
||||
bool webpSmartSubsample;
|
||||
VipsForeignWebpPreset webpPreset;
|
||||
int webpEffort;
|
||||
bool webpMinSize;
|
||||
bool webpMixed;
|
||||
@@ -318,6 +319,7 @@ struct PipelineBaton {
|
||||
webpNearLossless(false),
|
||||
webpLossless(false),
|
||||
webpSmartSubsample(false),
|
||||
webpPreset(VIPS_FOREIGN_WEBP_PRESET_DEFAULT),
|
||||
webpEffort(4),
|
||||
webpMinSize(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("libvipsVersion", Napi::Function::New(env, libvipsVersion));
|
||||
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("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
|
||||
exports.Set("stats", Napi::Function::New(env, stats));
|
||||
|
||||
@@ -164,6 +164,17 @@ Napi::Value format(const Napi::CallbackInfo& info) {
|
||||
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.
|
||||
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 libvipsVersion(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 _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
|
||||
|
||||
# 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
|
||||
|
||||
# Install benchmark dependencies
|
||||
@@ -23,4 +23,9 @@ RUN cat /etc/os-release | grep VERSION=
|
||||
RUN node -v
|
||||
|
||||
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" ]
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@squoosh/cli": "0.7.3",
|
||||
"@squoosh/lib": "0.4.0",
|
||||
"@squoosh/lib": "0.5.3",
|
||||
"async": "3.2.4",
|
||||
"benchmark": "2.1.4",
|
||||
"gm": "1.25.0",
|
||||
"imagemagick": "0.1.3",
|
||||
"jimp": "0.22.7"
|
||||
"jimp": "0.22.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs-node": "4.2.0",
|
||||
"@tensorflow/tfjs-node": "4.9.0",
|
||||
"mapnik": "4.5.9"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -109,7 +109,7 @@ async.series({
|
||||
jpegSuite.add('squoosh-lib-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
const pool = new squoosh.ImagePool();
|
||||
const pool = new squoosh.ImagePool(os.cpus().length);
|
||||
const image = pool.ingestImage(inputJpgBuffer);
|
||||
image.decoded
|
||||
.then(function () {
|
||||
@@ -652,7 +652,7 @@ async.series({
|
||||
throw err;
|
||||
} else {
|
||||
image
|
||||
.resize(width, heightPng)
|
||||
.resize(width, heightPng, jimp.RESIZE_BICUBIC)
|
||||
.deflateLevel(6)
|
||||
.filterType(0)
|
||||
.getBuffer(jimp.MIME_PNG, function (err) {
|
||||
@@ -673,7 +673,7 @@ async.series({
|
||||
throw err;
|
||||
} else {
|
||||
image
|
||||
.resize(width, heightPng)
|
||||
.resize(width, heightPng, jimp.RESIZE_BICUBIC)
|
||||
.deflateLevel(6)
|
||||
.filterType(0)
|
||||
.write(outputPng, function (err) {
|
||||
@@ -707,7 +707,7 @@ async.series({
|
||||
pngSuite.add('squoosh-lib-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
const pool = new squoosh.ImagePool();
|
||||
const pool = new squoosh.ImagePool(os.cpus().length);
|
||||
const image = pool.ingestImage(inputPngBuffer);
|
||||
image.decoded
|
||||
.then(function () {
|
||||
|
||||
@@ -524,7 +524,7 @@ sharp('input.tiff').jxl({ lossless: true }).toFile('out.jxl');
|
||||
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
|
||||
|
||||
// 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
|
||||
sharp('input.tiff', { failOn: 'none' });
|
||||
@@ -651,3 +651,13 @@ sharp(input).composite([
|
||||
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) {
|
||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||
.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) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240, { fit: sharp.fit.fill })
|
||||
@@ -489,4 +506,28 @@ describe('Rotation', function () {
|
||||
.timeout({ seconds: 5 })
|
||||
.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;
|
||||
assert.strictEqual(true, stat.isFile());
|
||||
assert.strictEqual(true, stat.size > 0);
|
||||
extractZip(container, { dir: path.dirname(extractTo) })
|
||||
extractZip(container, { dir: extractTo })
|
||||
.then(() => {
|
||||
assertDeepZoomTiles(directory, 256, 13, done);
|
||||
})
|
||||
@@ -959,7 +959,7 @@ describe('Tile', function () {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, stat.isFile());
|
||||
assert.strictEqual(true, stat.size > 0);
|
||||
extractZip(container, { dir: path.dirname(extractTo) })
|
||||
extractZip(container, { dir: extractTo })
|
||||
.then(() => {
|
||||
assertDeepZoomTiles(directory, 256, 13, done);
|
||||
})
|
||||
@@ -988,7 +988,7 @@ describe('Tile', function () {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, stat.isFile());
|
||||
assert.strictEqual(true, stat.size > 0);
|
||||
extractZip(container, { dir: path.dirname(extractTo) })
|
||||
extractZip(container, { dir: extractTo })
|
||||
.then(() => {
|
||||
assertDeepZoomTiles(directory, 256, 13, done);
|
||||
})
|
||||
|
||||
@@ -153,4 +153,41 @@ describe('Utilities', function () {
|
||||
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', () =>
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
|
||||
Reference in New Issue
Block a user