mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df6efa0285 | ||
|
|
3c10e118e3 | ||
|
|
19980190f7 | ||
|
|
8f5495a446 | ||
|
|
9431029917 | ||
|
|
17ea70a102 | ||
|
|
7f142bddb3 | ||
|
|
98e0516ac1 | ||
|
|
7717516d1d | ||
|
|
760550ca0d | ||
|
|
f8144dd89c | ||
|
|
ac4070cb49 | ||
|
|
25b964e3df | ||
|
|
17ec6c72df | ||
|
|
a7b1185602 | ||
|
|
c76ae06fd1 | ||
|
|
b3dd54d550 | ||
|
|
d248eadb06 | ||
|
|
507eef3053 | ||
|
|
fc2f672337 | ||
|
|
6f49be8f26 |
36
binding.gyp
36
binding.gyp
@@ -29,10 +29,22 @@
|
|||||||
'Release': {
|
'Release': {
|
||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'ExceptionHandling': 1
|
'ExceptionHandling': 1,
|
||||||
|
'WholeProgramOptimization': 'true'
|
||||||
|
},
|
||||||
|
'VCLibrarianTool': {
|
||||||
|
'AdditionalOptions': [
|
||||||
|
'/LTCG:INCREMENTAL'
|
||||||
|
]
|
||||||
},
|
},
|
||||||
'VCLinkerTool': {
|
'VCLinkerTool': {
|
||||||
'ImageHasSafeExceptionHandlers': 'false'
|
'ImageHasSafeExceptionHandlers': 'false',
|
||||||
|
'OptimizeReferences': 2,
|
||||||
|
'EnableCOMDATFolding': 2,
|
||||||
|
'LinkIncremental': 1,
|
||||||
|
'AdditionalOptions': [
|
||||||
|
'/LTCG:INCREMENTAL'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'msvs_disabled_warnings': [
|
'msvs_disabled_warnings': [
|
||||||
@@ -121,7 +133,7 @@
|
|||||||
'../vendor/lib/libglib-2.0.0.dylib',
|
'../vendor/lib/libglib-2.0.0.dylib',
|
||||||
'../vendor/lib/libgobject-2.0.0.dylib',
|
'../vendor/lib/libgobject-2.0.0.dylib',
|
||||||
# Ensure runtime linking is relative to sharp.node
|
# Ensure runtime linking is relative to sharp.node
|
||||||
'-rpath \'@loader_path/../../vendor/lib\''
|
'-Wl,-s -rpath \'@loader_path/../../vendor/lib\''
|
||||||
]
|
]
|
||||||
}],
|
}],
|
||||||
['OS == "linux"', {
|
['OS == "linux"', {
|
||||||
@@ -164,7 +176,7 @@
|
|||||||
'../vendor/lib/libxml2.so',
|
'../vendor/lib/libxml2.so',
|
||||||
'../vendor/lib/libz.so',
|
'../vendor/lib/libz.so',
|
||||||
# Ensure runtime linking is relative to sharp.node
|
# Ensure runtime linking is relative to sharp.node
|
||||||
'-Wl,--disable-new-dtags -Wl,-rpath=\'$${ORIGIN}/../../vendor/lib\''
|
'-Wl,-s -Wl,--disable-new-dtags -Wl,-rpath=\'$${ORIGIN}/../../vendor/lib\''
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
@@ -204,10 +216,22 @@
|
|||||||
['OS == "win"', {
|
['OS == "win"', {
|
||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'ExceptionHandling': 1
|
'ExceptionHandling': 1,
|
||||||
|
'WholeProgramOptimization': 'true'
|
||||||
|
},
|
||||||
|
'VCLibrarianTool': {
|
||||||
|
'AdditionalOptions': [
|
||||||
|
'/LTCG:INCREMENTAL'
|
||||||
|
]
|
||||||
},
|
},
|
||||||
'VCLinkerTool': {
|
'VCLinkerTool': {
|
||||||
'ImageHasSafeExceptionHandlers': 'false'
|
'ImageHasSafeExceptionHandlers': 'false',
|
||||||
|
'OptimizeReferences': 2,
|
||||||
|
'EnableCOMDATFolding': 2,
|
||||||
|
'LinkIncremental': 1,
|
||||||
|
'AdditionalOptions': [
|
||||||
|
'/LTCG:INCREMENTAL'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'msvs_disabled_warnings': [
|
'msvs_disabled_warnings': [
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Extract a single channel from a multi-channel image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `channel` **([number][1] \| [string][2])** zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
|
- `channel` **([number][1] \| [string][2])** zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
|||||||
@@ -2,32 +2,42 @@
|
|||||||
|
|
||||||
## Sharp
|
## Sharp
|
||||||
|
|
||||||
|
Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
||||||
|
|
||||||
|
JPEG, PNG, WebP or TIFF format image data can be streamed out from this object.
|
||||||
|
When using Stream based output, derived attributes are available from the `info` event.
|
||||||
|
|
||||||
|
Non-critical problems encountered during processing are emitted as `warning` events.
|
||||||
|
|
||||||
|
Implements the [stream.Duplex][1] class.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `input` **([Buffer][1] \| [String][2])?** if present, can be
|
- `input` **([Buffer][2] \| [string][3])?** if present, can be
|
||||||
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
||||||
a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
||||||
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||||
- `options` **[Object][3]?** if present, is an Object with optional attributes.
|
- `options` **[Object][4]?** if present, is an Object with optional attributes.
|
||||||
- `options.failOnError` **[Boolean][4]** by default halt processing and raise an error when loading invalid images.
|
- `options.failOnError` **[boolean][5]** by default halt processing and raise an error when loading invalid images.
|
||||||
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
|
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
|
||||||
- `options.limitInputPixels` **([Number][5] \| [Boolean][4])** Do not process input images where the number of pixels
|
- `options.limitInputPixels` **([number][6] \| [boolean][5])** 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). (optional, default `268402689`)
|
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
|
||||||
- `options.sequentialRead` **[Boolean][4]** Set this to `true` to use sequential rather than random access where possible.
|
- `options.sequentialRead` **[boolean][5]** Set this to `true` to use sequential rather than random access where possible.
|
||||||
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
||||||
- `options.density` **[Number][5]** number representing the DPI for vector images. (optional, default `72`)
|
- `options.density` **[number][6]** number representing the DPI for vector images. (optional, default `72`)
|
||||||
- `options.pages` **[Number][5]** number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
- `options.pages` **[number][6]** number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
||||||
- `options.page` **[Number][5]** page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default `0`)
|
- `options.page` **[number][6]** page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||||
- `options.raw` **[Object][3]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
- `options.level` **[number][6]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||||
- `options.raw.width` **[Number][5]?**
|
- `options.raw` **[Object][4]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
- `options.raw.height` **[Number][5]?**
|
- `options.raw.width` **[number][6]?**
|
||||||
- `options.raw.channels` **[Number][5]?** 1-4
|
- `options.raw.height` **[number][6]?**
|
||||||
- `options.create` **[Object][3]?** describes a new image to be created.
|
- `options.raw.channels` **[number][6]?** 1-4
|
||||||
- `options.create.width` **[Number][5]?**
|
- `options.create` **[Object][4]?** describes a new image to be created.
|
||||||
- `options.create.height` **[Number][5]?**
|
- `options.create.width` **[number][6]?**
|
||||||
- `options.create.channels` **[Number][5]?** 3-4
|
- `options.create.height` **[number][6]?**
|
||||||
- `options.create.background` **([String][2] \| [Object][3])?** parsed by the [color][6] module to extract values for red, green, blue and alpha.
|
- `options.create.channels` **[number][6]?** 3-4
|
||||||
|
- `options.create.background` **([string][3] \| [Object][4])?** parsed by the [color][7] module to extract values for red, green, blue and alpha.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -68,9 +78,9 @@ sharp({
|
|||||||
.then( ... );
|
.then( ... );
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][7]** Invalid parameters
|
- Throws **[Error][8]** Invalid parameters
|
||||||
|
|
||||||
Returns **[Sharp][8]**
|
Returns **[Sharp][9]**
|
||||||
|
|
||||||
## clone
|
## clone
|
||||||
|
|
||||||
@@ -138,20 +148,22 @@ Promise.all(promises)
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns **[Sharp][8]**
|
Returns **[Sharp][9]**
|
||||||
|
|
||||||
[1]: https://nodejs.org/api/buffer.html
|
[1]: http://nodejs.org/api/stream.html#stream_class_stream_duplex
|
||||||
|
|
||||||
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
[2]: https://nodejs.org/api/buffer.html
|
||||||
|
|
||||||
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||||
|
|
||||||
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||||
|
|
||||||
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||||
|
|
||||||
[6]: https://www.npmjs.org/package/color
|
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||||
|
|
||||||
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
[7]: https://www.npmjs.org/package/color
|
||||||
|
|
||||||
[8]: #sharp
|
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||||
|
|
||||||
|
[9]: #sharp
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
- `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
|
||||||
|
- `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
||||||
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
- `orientation`: Number value of the EXIF Orientation header, if present
|
- `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
@@ -70,6 +71,7 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
||||||
- `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
- `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
||||||
- `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
- `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
||||||
|
- `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
@@ -86,6 +88,10 @@ image
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const { entropy, sharpness } = await sharp(input).stats();
|
||||||
|
```
|
||||||
|
|
||||||
Returns **[Promise][5]<[Object][6]>**
|
Returns **[Promise][5]<[Object][6]>**
|
||||||
|
|
||||||
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
|
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
|
||||||
|
|||||||
@@ -140,20 +140,22 @@ Returns **Sharp**
|
|||||||
|
|
||||||
Use these JPEG options for output image.
|
Use these JPEG options for output image.
|
||||||
|
|
||||||
|
Some of these options require the use of a globally-installed libvips compiled with support for mozjpeg.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
- `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
- `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.chromaSubsampling` **[string][2]** for quality < 90, set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' (use chroma subsampling); for quality >= 90 chroma is never subsampled (optional, default `'4:2:0'`)
|
- `options.chromaSubsampling` **[string][2]** for quality < 90, set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' (use chroma subsampling); for quality >= 90 chroma is never subsampled (optional, default `'4:2:0'`)
|
||||||
|
- `options.optimiseCoding` **[boolean][7]** optimise Huffman coding tables (optional, default `true`)
|
||||||
|
- `options.optimizeCoding` **[boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
|
||||||
- `options.trellisQuantisation` **[boolean][7]** apply trellis quantisation, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
- `options.trellisQuantisation` **[boolean][7]** apply trellis quantisation, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.overshootDeringing` **[boolean][7]** apply overshoot deringing, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
- `options.overshootDeringing` **[boolean][7]** apply overshoot deringing, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.optimiseScans` **[boolean][7]** optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
- `options.optimiseScans` **[boolean][7]** optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.optimizeScans` **[boolean][7]** alternative spelling of optimiseScans (optional, default `false`)
|
- `options.optimizeScans` **[boolean][7]** alternative spelling of optimiseScans, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.optimiseCoding` **[boolean][7]** optimise Huffman coding tables (optional, default `true`)
|
|
||||||
- `options.optimizeCoding` **[boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
|
|
||||||
- `options.quantisationTable` **[number][9]** quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg (optional, default `0`)
|
- `options.quantisationTable` **[number][9]** quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg (optional, default `0`)
|
||||||
- `options.quantizationTable` **[number][9]** alternative spelling of quantisationTable (optional, default `0`)
|
- `options.quantizationTable` **[number][9]** alternative spelling of quantisationTable, requires libvips compiled with support for mozjpeg (optional, default `0`)
|
||||||
- `options.force` **[boolean][7]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
- `options.force` **[boolean][7]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
@@ -179,6 +181,8 @@ Use these PNG options for output image.
|
|||||||
PNG output is always full colour at 8 or 16 bits per pixel.
|
PNG output is always full colour at 8 or 16 bits per pixel.
|
||||||
Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
||||||
|
|
||||||
|
Some of these options require the use of a globally-installed libvips compiled with support for libimagequant (GPL).
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?**
|
- `options` **[Object][6]?**
|
||||||
@@ -186,10 +190,10 @@ Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
|||||||
- `options.compressionLevel` **[number][9]** zlib compression level, 0-9 (optional, default `9`)
|
- `options.compressionLevel` **[number][9]** zlib compression level, 0-9 (optional, default `9`)
|
||||||
- `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
|
- `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
|
||||||
- `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant (optional, default `false`)
|
- `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant (optional, default `false`)
|
||||||
- `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, requires libvips compiled with support for libimagequant (optional, default `100`)
|
- `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true`, requires libvips compiled with support for libimagequant (optional, default `100`)
|
||||||
- `options.colours` **[number][9]** maximum number of palette entries, requires libvips compiled with support for libimagequant (optional, default `256`)
|
- `options.colours` **[number][9]** maximum number of palette entries, sets `palette` to `true`, requires libvips compiled with support for libimagequant (optional, default `256`)
|
||||||
- `options.colors` **[number][9]** alternative spelling of `options.colours`, requires libvips compiled with support for libimagequant (optional, default `256`)
|
- `options.colors` **[number][9]** alternative spelling of `options.colours`, sets `palette` to `true`, requires libvips compiled with support for libimagequant (optional, default `256`)
|
||||||
- `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, requires libvips compiled with support for libimagequant (optional, default `1.0`)
|
- `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, sets `palette` to `true`, requires libvips compiled with support for libimagequant (optional, default `1.0`)
|
||||||
- `options.force` **[boolean][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
- `options.force` **[boolean][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
@@ -4,6 +4,29 @@
|
|||||||
|
|
||||||
Requires libvips v8.9.1
|
Requires libvips v8.9.1
|
||||||
|
|
||||||
|
### v0.25.4 - 12th June 2020
|
||||||
|
|
||||||
|
* Allow libvips binary location override where version is appended.
|
||||||
|
[#2217](https://github.com/lovell/sharp/pull/2217)
|
||||||
|
[@malice00](https://github.com/malice00)
|
||||||
|
|
||||||
|
* Enable PNG palette when setting quality, colours, colors or dither.
|
||||||
|
[#2226](https://github.com/lovell/sharp/pull/2226)
|
||||||
|
[@romaleev](https://github.com/romaleev)
|
||||||
|
|
||||||
|
* Add `level` constructor option to use a specific level of a multi-level image.
|
||||||
|
Expose `levels` metadata for multi-level images.
|
||||||
|
[#2222](https://github.com/lovell/sharp/issues/2222)
|
||||||
|
|
||||||
|
* Add support for named `alpha` channel to `extractChannel` operation.
|
||||||
|
[#2138](https://github.com/lovell/sharp/issues/2138)
|
||||||
|
|
||||||
|
* Add experimental `sharpness` calculation to `stats()` response.
|
||||||
|
[#2251](https://github.com/lovell/sharp/issues/2251)
|
||||||
|
|
||||||
|
* Emit `warning` event for non-critical processing problems.
|
||||||
|
[#2032](https://github.com/lovell/sharp/issues/2032)
|
||||||
|
|
||||||
### v0.25.3 - 17th May 2020
|
### v0.25.3 - 17th May 2020
|
||||||
|
|
||||||
* Ensure libvips is initialised only once, improves worker thread safety.
|
* Ensure libvips is initialised only once, improves worker thread safety.
|
||||||
|
|||||||
@@ -182,3 +182,9 @@ GitHub: https://github.com/edsilv
|
|||||||
|
|
||||||
Name: Dumitru Deveatii
|
Name: Dumitru Deveatii
|
||||||
GitHub: https://github.com/dimadeveatii
|
GitHub: https://github.com/dimadeveatii
|
||||||
|
|
||||||
|
Name: Roland Asmann
|
||||||
|
GitHub: https://github.com/malice00
|
||||||
|
|
||||||
|
Name: Roman Malieiev
|
||||||
|
GitHub: https://github.com/romaleev
|
||||||
|
|||||||
@@ -12,10 +12,11 @@
|
|||||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://pixel.plumbing/px/114x114/sharp-logo.svg">
|
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://pixel.plumbing/px/114x114/sharp-logo.svg">
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
|
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
|
||||||
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.css">
|
<style>/* https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.css */ body{margin:0;color:var(--text-color);background:var(--page-background);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font:16px/1.7 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}*{box-sizing:border-box}a{color:var(--link-color);text-decoration:none}.page-content>:first-child{margin-top:0}.page-content.has-page-title>h2:first-child{margin-top:7rem}.page-content h2,.page-content h3{font-weight:300;line-height:1.2}.page-content h2{font-size:2rem;border-bottom:1px solid var(--border-color);margin-top:7rem;padding-bottom:5px}.page-content h3{font-size:1.7rem;margin:40px 0 30px}.page-content code{font-family:var(--code-font);font-size:90%;background:var(--inline-code-background);border-radius:4px;padding:3px 5px;color:var(--inline-code-color)}.page-content>ul{padding-left:20px;margin:1rem 0}.page-content img{max-width:100%}@-webkit-keyframes blink-data-v-4f620c69{to{opacity:.2}}@keyframes blink-data-v-4f620c69{0%{opacity:.2}20%{opacity:1}to{opacity:.2}}</style>
|
||||||
<link rel="author" href="/humans.txt" type="text/plain">
|
<link rel="author" href="/humans.txt" type="text/plain">
|
||||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.3/docs/README.md" as="fetch" type="text/markdown" crossorigin>
|
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.4/docs/README.md" as="fetch" type="text/markdown" crossorigin>
|
||||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
|
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
|
||||||
|
<link rel="dns-prefetch" href="https://pixel.plumbing">
|
||||||
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{
|
{
|
||||||
@@ -65,8 +66,8 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="docute"></div>
|
<div id="docute"></div>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docute-google-analytics@1/dist/index.min.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
|
/* https://cdn.jsdelivr.net/npm/docute-google-analytics@1/dist/index.min.js */ !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).docuteGoogleAnalytics=n()}(this,function(){"use strict";function e(e){var n;window.ga||((n=document.createElement("script")).async=!0,n.src="https://www.google-analytics.com/analytics.js",document.body.appendChild(n),window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)},ga.l=Number(new Date),ga("create",e,"auto"))}function n(n,t){e(t),ga("set","page",n),ga("send","pageview")}return function(e){return{name:"@google-analytics",extend:function(t){!function(e,t){"function"==typeof e?e(function(e){n(e,t)}):e.afterEach(function(e){n(e.fullPath,t)})}(t.router,e)}}}});
|
||||||
const docuteApiTitlePlugin = {
|
const docuteApiTitlePlugin = {
|
||||||
name: 'apiTitle',
|
name: 'apiTitle',
|
||||||
extend(api) {
|
extend(api) {
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
docuteApiTitlePlugin,
|
docuteApiTitlePlugin,
|
||||||
docuteApiSearchPlugin
|
docuteApiSearchPlugin
|
||||||
],
|
],
|
||||||
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.3/docs',
|
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.4/docs',
|
||||||
nav: [
|
nav: [
|
||||||
{
|
{
|
||||||
title: 'Funding',
|
title: 'Funding',
|
||||||
|
|||||||
@@ -81,24 +81,41 @@ Building from source requires:
|
|||||||
|
|
||||||
This is an advanced approach that most people will not require.
|
This is an advanced approach that most people will not require.
|
||||||
|
|
||||||
To install the prebuilt libvips binaries from a custom URL,
|
To install the prebuilt sharp binaries from a custom URL,
|
||||||
set the `sharp_dist_base_url` npm config option
|
set the `sharp_binary_host` npm config option
|
||||||
or the `SHARP_DIST_BASE_URL` environment variable.
|
or the `npm_config_sharp_binary_host` environment variable.
|
||||||
|
|
||||||
For example, both of the following will result in an attempt to download the file located at
|
To install the prebuilt libvips binaries from a custom URL,
|
||||||
`https://hostname/path/libvips-x.y.z-platform.tar.gz`.
|
set the `sharp_libvips_binary_host` npm config option
|
||||||
|
or the `npm_config_sharp_libvips_binary_host` environment variable.
|
||||||
|
|
||||||
|
The version subpath and file name are appended to these.
|
||||||
|
|
||||||
|
For example, if `sharp_libvips_binary_host` is set to `https://hostname/path`
|
||||||
|
and the libvips version is `1.2.3` then the resultant URL will be
|
||||||
|
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.gz`.
|
||||||
|
|
||||||
|
See the Chinese mirror below for a further example.
|
||||||
|
|
||||||
|
## Chinese mirror
|
||||||
|
|
||||||
|
Alibaba provide a mirror site based in China containing binaries for both sharp and libvips.
|
||||||
|
|
||||||
|
To use this either set the following configuration:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm config set sharp_dist_base_url "https://hostname/path/"
|
npm config set sharp_binary_host "https://npm.taobao.org/mirrors/sharp"
|
||||||
|
npm config set sharp_libvips_binary_host "https://npm.taobao.org/mirrors/sharp-libvips"
|
||||||
npm install sharp
|
npm install sharp
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
or set the following environment variables:
|
||||||
SHARP_DIST_BASE_URL="https://hostname/path/" npm install sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
To install the prebuilt sharp binaries from a custom URL, please see
|
```sh
|
||||||
[https://github.com/prebuild/prebuild-install#custom-binaries](https://github.com/prebuild/prebuild-install#custom-binaries)
|
npm_config_sharp_binary_host="https://npm.taobao.org/mirrors/sharp" \
|
||||||
|
npm_config_sharp_libvips_binary_host "https://npm.taobao.org/mirrors/sharp-libvips" \
|
||||||
|
npm install sharp
|
||||||
|
```
|
||||||
|
|
||||||
## FreeBSD
|
## FreeBSD
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -21,7 +21,8 @@ const minimumGlibcVersionByArch = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
||||||
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersionLabelled}/`;
|
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
||||||
|
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
||||||
|
|
||||||
const fail = function (err) {
|
const fail = function (err) {
|
||||||
npmLog.error('sharp', err.message);
|
npmLog.error('sharp', err.message);
|
||||||
|
|||||||
@@ -60,22 +60,19 @@ function ensureAlpha () {
|
|||||||
* // green.jpg is a greyscale image containing the green channel of the input
|
* // green.jpg is a greyscale image containing the green channel of the input
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* @param {number|string} channel - zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
|
* @param {number|string} channel - zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid channel
|
* @throws {Error} Invalid channel
|
||||||
*/
|
*/
|
||||||
function extractChannel (channel) {
|
function extractChannel (channel) {
|
||||||
if (channel === 'red') {
|
const channelMap = { red: 0, green: 1, blue: 2, alpha: 3 };
|
||||||
channel = 0;
|
if (Object.keys(channelMap).includes(channel)) {
|
||||||
} else if (channel === 'green') {
|
channel = channelMap[channel];
|
||||||
channel = 1;
|
|
||||||
} else if (channel === 'blue') {
|
|
||||||
channel = 2;
|
|
||||||
}
|
}
|
||||||
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
|
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
|
||||||
this.options.extractChannel = channel;
|
this.options.extractChannel = channel;
|
||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue', channel);
|
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue, alpha', channel);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,15 +38,20 @@ try {
|
|||||||
const debuglog = util.debuglog('sharp');
|
const debuglog = util.debuglog('sharp');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructs sharp
|
|
||||||
*
|
|
||||||
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
||||||
*
|
*
|
||||||
* JPEG, PNG, WebP or TIFF format image data can be streamed out from this object.
|
* JPEG, PNG, WebP or TIFF format image data can be streamed out from this object.
|
||||||
* When using Stream based output, derived attributes are available from the `info` event.
|
* When using Stream based output, derived attributes are available from the `info` event.
|
||||||
*
|
*
|
||||||
|
* Non-critical problems encountered during processing are emitted as `warning` events.
|
||||||
|
*
|
||||||
* Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
|
* Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
|
||||||
*
|
*
|
||||||
|
* @constructs Sharp
|
||||||
|
*
|
||||||
|
* @emits Sharp#info
|
||||||
|
* @emits Sharp#warning
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* sharp('input.jpg')
|
* sharp('input.jpg')
|
||||||
* .resize(300, 200)
|
* .resize(300, 200)
|
||||||
@@ -81,30 +86,31 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* .toBuffer()
|
* .toBuffer()
|
||||||
* .then( ... );
|
* .then( ... );
|
||||||
*
|
*
|
||||||
* @param {(Buffer|String)} [input] - if present, can be
|
* @param {(Buffer|string)} [input] - if present, can be
|
||||||
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
||||||
* a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
* a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
||||||
* JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
* JPEG, PNG, WebP, 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 {Boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
|
* @param {boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
|
||||||
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
|
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
|
||||||
* @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).
|
||||||
* @param {Boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
|
* @param {boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
|
||||||
* This can reduce memory usage and might improve performance on some systems.
|
* This can reduce memory usage and might improve performance on some systems.
|
||||||
* @param {Number} [options.density=72] - number representing the DPI for vector images.
|
* @param {number} [options.density=72] - number representing the DPI for vector images.
|
||||||
* @param {Number} [options.pages=1] - number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages.
|
* @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages.
|
||||||
* @param {Number} [options.page=0] - page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based.
|
* @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based.
|
||||||
|
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
||||||
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
* @param {Number} [options.raw.width]
|
* @param {number} [options.raw.width]
|
||||||
* @param {Number} [options.raw.height]
|
* @param {number} [options.raw.height]
|
||||||
* @param {Number} [options.raw.channels] - 1-4
|
* @param {number} [options.raw.channels] - 1-4
|
||||||
* @param {Object} [options.create] - describes a new image to be created.
|
* @param {Object} [options.create] - describes a new image to be created.
|
||||||
* @param {Number} [options.create.width]
|
* @param {number} [options.create.width]
|
||||||
* @param {Number} [options.create.height]
|
* @param {number} [options.create.height]
|
||||||
* @param {Number} [options.create.channels] - 3-4
|
* @param {number} [options.create.channels] - 3-4
|
||||||
* @param {String|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
* @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@@ -229,7 +235,10 @@ const Sharp = function (input, options) {
|
|||||||
linearA: 1,
|
linearA: 1,
|
||||||
linearB: 0,
|
linearB: 0,
|
||||||
// Function to notify of libvips warnings
|
// Function to notify of libvips warnings
|
||||||
debuglog: debuglog,
|
debuglog: warning => {
|
||||||
|
this.emit('warning', warning);
|
||||||
|
debuglog(warning);
|
||||||
|
},
|
||||||
// Function to notify of queue length changes
|
// Function to notify of queue length changes
|
||||||
queueListener: function (queueLength) {
|
queueListener: function (queueLength) {
|
||||||
Sharp.queue.emit('change', queueLength);
|
Sharp.queue.emit('change', queueLength);
|
||||||
|
|||||||
13
lib/input.js
13
lib/input.js
@@ -113,6 +113,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
|
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Multi-level input (OpenSlide)
|
||||||
|
if (is.defined(inputOptions.level)) {
|
||||||
|
if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) {
|
||||||
|
inputDescriptor.level = inputOptions.level;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Create new image
|
// Create new image
|
||||||
if (is.defined(inputOptions.create)) {
|
if (is.defined(inputOptions.create)) {
|
||||||
if (
|
if (
|
||||||
@@ -208,6 +216,7 @@ function _isStreamInput () {
|
|||||||
* - `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
|
||||||
|
* - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
||||||
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
* - `orientation`: Number value of the EXIF Orientation header, if present
|
* - `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
@@ -290,6 +299,7 @@ function metadata (callback) {
|
|||||||
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
||||||
* - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
* - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
||||||
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
||||||
|
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const image = sharp(inputJpg);
|
* const image = sharp(inputJpg);
|
||||||
@@ -299,6 +309,9 @@ function metadata (callback) {
|
|||||||
* // stats contains the channel-wise statistics array and the isOpaque value
|
* // stats contains the channel-wise statistics array and the isOpaque value
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
|
* @example
|
||||||
|
* const { entropy, sharpness } = await sharp(input).stats();
|
||||||
|
*
|
||||||
* @param {Function} [callback] - called with the arguments `(err, stats)`
|
* @param {Function} [callback] - called with the arguments `(err, stats)`
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const object = function (val) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
const plainObject = function (val) {
|
const plainObject = function (val) {
|
||||||
return object(val) && Object.prototype.toString.call(val) === '[object Object]';
|
return Object.prototype.toString.call(val) === '[object Object]';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +45,7 @@ const bool = function (val) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
const buffer = function (val) {
|
const buffer = function (val) {
|
||||||
return object(val) && val instanceof Buffer;
|
return val instanceof Buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,7 +69,7 @@ const number = function (val) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
const integer = function (val) {
|
const integer = function (val) {
|
||||||
return number(val) && val % 1 === 0;
|
return Number.isInteger(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,7 +85,7 @@ const inRange = function (val, min, max) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
const inArray = function (val, list) {
|
const inArray = function (val, list) {
|
||||||
return list.indexOf(val) !== -1;
|
return list.includes(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const hasVendoredLibvips = function () {
|
|||||||
if (currentPlatformId === vendorPlatformId) {
|
if (currentPlatformId === vendorPlatformId) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
|
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp' directory and run 'npm install' on the '${currentPlatformId}' platform.`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -173,6 +173,8 @@ function toFormat (format, options) {
|
|||||||
/**
|
/**
|
||||||
* Use these JPEG options for output image.
|
* Use these JPEG options for output image.
|
||||||
*
|
*
|
||||||
|
* Some of these options require the use of a globally-installed libvips compiled with support for mozjpeg.
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Convert any input to very high quality JPEG output
|
* // Convert any input to very high quality JPEG output
|
||||||
* const data = await sharp(input)
|
* const data = await sharp(input)
|
||||||
@@ -186,14 +188,14 @@ function toFormat (format, options) {
|
|||||||
* @param {number} [options.quality=80] - quality, integer 1-100
|
* @param {number} [options.quality=80] - quality, integer 1-100
|
||||||
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
|
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
|
||||||
* @param {string} [options.chromaSubsampling='4:2:0'] - for quality < 90, set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' (use chroma subsampling); for quality >= 90 chroma is never subsampled
|
* @param {string} [options.chromaSubsampling='4:2:0'] - for quality < 90, set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' (use chroma subsampling); for quality >= 90 chroma is never subsampled
|
||||||
|
* @param {boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
|
||||||
|
* @param {boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
|
||||||
* @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires libvips compiled with support for mozjpeg
|
* @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires libvips compiled with support for mozjpeg
|
||||||
* @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires libvips compiled with support for mozjpeg
|
* @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires libvips compiled with support for mozjpeg
|
||||||
* @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg
|
* @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg
|
||||||
* @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
|
* @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans, requires libvips compiled with support for mozjpeg
|
||||||
* @param {boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
|
|
||||||
* @param {boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
|
|
||||||
* @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg
|
* @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg
|
||||||
* @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable
|
* @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable, requires libvips compiled with support for mozjpeg
|
||||||
* @param {boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
|
* @param {boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
@@ -253,6 +255,8 @@ function jpeg (options) {
|
|||||||
* PNG output is always full colour at 8 or 16 bits per pixel.
|
* PNG output is always full colour at 8 or 16 bits per pixel.
|
||||||
* Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
* Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
||||||
*
|
*
|
||||||
|
* Some of these options require the use of a globally-installed libvips compiled with support for libimagequant (GPL).
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Convert any input to PNG output
|
* // Convert any input to PNG output
|
||||||
* const data = await sharp(input)
|
* const data = await sharp(input)
|
||||||
@@ -264,10 +268,10 @@ function jpeg (options) {
|
|||||||
* @param {number} [options.compressionLevel=9] - zlib compression level, 0-9
|
* @param {number} [options.compressionLevel=9] - zlib compression level, 0-9
|
||||||
* @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
* @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
||||||
* @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant
|
* @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant
|
||||||
* @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libvips compiled with support for libimagequant
|
* @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`, requires libvips compiled with support for libimagequant
|
||||||
* @param {number} [options.colours=256] - maximum number of palette entries, requires libvips compiled with support for libimagequant
|
* @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`, requires libvips compiled with support for libimagequant
|
||||||
* @param {number} [options.colors=256] - alternative spelling of `options.colours`, requires libvips compiled with support for libimagequant
|
* @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`, requires libvips compiled with support for libimagequant
|
||||||
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libvips compiled with support for libimagequant
|
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`, requires libvips compiled with support for libimagequant
|
||||||
* @param {boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
* @param {boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
@@ -289,28 +293,30 @@ function png (options) {
|
|||||||
}
|
}
|
||||||
if (is.defined(options.palette)) {
|
if (is.defined(options.palette)) {
|
||||||
this._setBooleanOption('pngPalette', options.palette);
|
this._setBooleanOption('pngPalette', options.palette);
|
||||||
if (this.options.pngPalette) {
|
} else if (is.defined(options.quality) || is.defined(options.colours || options.colors) || is.defined(options.dither)) {
|
||||||
if (is.defined(options.quality)) {
|
this._setBooleanOption('pngPalette', true);
|
||||||
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
|
}
|
||||||
this.options.pngQuality = options.quality;
|
if (this.options.pngPalette) {
|
||||||
} else {
|
if (is.defined(options.quality)) {
|
||||||
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
|
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
|
||||||
}
|
this.options.pngQuality = options.quality;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
|
||||||
}
|
}
|
||||||
const colours = options.colours || options.colors;
|
}
|
||||||
if (is.defined(colours)) {
|
const colours = options.colours || options.colors;
|
||||||
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
if (is.defined(colours)) {
|
||||||
this.options.pngColours = colours;
|
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
||||||
} else {
|
this.options.pngColours = colours;
|
||||||
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
} else {
|
||||||
}
|
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
||||||
}
|
}
|
||||||
if (is.defined(options.dither)) {
|
}
|
||||||
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
|
if (is.defined(options.dither)) {
|
||||||
this.options.pngDither = options.dither;
|
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
|
||||||
} else {
|
this.options.pngDither = options.dither;
|
||||||
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
|
} else {
|
||||||
}
|
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
package.json
13
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 and TIFF images",
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"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": [
|
||||||
@@ -66,7 +66,8 @@
|
|||||||
"Paul Neave <paul.neave@gmail.com>",
|
"Paul Neave <paul.neave@gmail.com>",
|
||||||
"Brendan Kennedy <brenwken@gmail.com>",
|
"Brendan Kennedy <brenwken@gmail.com>",
|
||||||
"Brychan Bennett-Odlum <git@brychan.io>",
|
"Brychan Bennett-Odlum <git@brychan.io>",
|
||||||
"Edward Silverton <e.silverton@gmail.com>"
|
"Edward Silverton <e.silverton@gmail.com>",
|
||||||
|
"Roman Malieiev <aromaleev@gmail.com>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install --runtime=napi) || (node-gyp rebuild && node install/dll-copy)",
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install --runtime=napi) || (node-gyp rebuild && node install/dll-copy)",
|
||||||
@@ -112,7 +113,7 @@
|
|||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"node-addon-api": "^3.0.0",
|
"node-addon-api": "^3.0.0",
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"prebuild-install": "^5.3.3",
|
"prebuild-install": "^5.3.4",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"simple-get": "^4.0.0",
|
"simple-get": "^4.0.0",
|
||||||
"tar": "^6.0.2",
|
"tar": "^6.0.2",
|
||||||
@@ -122,13 +123,13 @@
|
|||||||
"async": "^3.2.0",
|
"async": "^3.2.0",
|
||||||
"cc": "^2.0.1",
|
"cc": "^2.0.1",
|
||||||
"decompress-zip": "^0.3.2",
|
"decompress-zip": "^0.3.2",
|
||||||
"documentation": "^13.0.0",
|
"documentation": "^13.0.1",
|
||||||
"exif-reader": "^1.0.3",
|
"exif-reader": "^1.0.3",
|
||||||
"icc": "^1.0.0",
|
"icc": "^1.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^7.1.2",
|
"mocha": "^8.0.1",
|
||||||
"mock-fs": "^4.12.0",
|
"mock-fs": "^4.12.0",
|
||||||
"nyc": "^15.0.1",
|
"nyc": "^15.1.0",
|
||||||
"prebuild": "^10.0.0",
|
"prebuild": "^10.0.0",
|
||||||
"prebuild-ci": "^3.1.0",
|
"prebuild-ci": "^3.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ namespace sharp {
|
|||||||
if (HasAttr(input, "page")) {
|
if (HasAttr(input, "page")) {
|
||||||
descriptor->page = AttrAsUint32(input, "page");
|
descriptor->page = AttrAsUint32(input, "page");
|
||||||
}
|
}
|
||||||
|
// Multi-level input (OpenSlide)
|
||||||
|
if (HasAttr(input, "level")) {
|
||||||
|
descriptor->level = AttrAsUint32(input, "level");
|
||||||
|
}
|
||||||
// Create new image
|
// Create new image
|
||||||
if (HasAttr(input, "createChannels")) {
|
if (HasAttr(input, "createChannels")) {
|
||||||
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
||||||
@@ -292,6 +296,9 @@ namespace sharp {
|
|||||||
option->set("n", descriptor->pages);
|
option->set("n", descriptor->pages);
|
||||||
option->set("page", descriptor->page);
|
option->set("page", descriptor->page);
|
||||||
}
|
}
|
||||||
|
if (imageType == ImageType::OPENSLIDE) {
|
||||||
|
option->set("level", descriptor->level);
|
||||||
|
}
|
||||||
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
image = SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
@@ -341,6 +348,9 @@ namespace sharp {
|
|||||||
option->set("n", descriptor->pages);
|
option->set("n", descriptor->pages);
|
||||||
option->set("page", descriptor->page);
|
option->set("page", descriptor->page);
|
||||||
}
|
}
|
||||||
|
if (imageType == ImageType::OPENSLIDE) {
|
||||||
|
option->set("level", descriptor->level);
|
||||||
|
}
|
||||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
image = SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ namespace sharp {
|
|||||||
int rawHeight;
|
int rawHeight;
|
||||||
int pages;
|
int pages;
|
||||||
int page;
|
int page;
|
||||||
|
int level;
|
||||||
int createChannels;
|
int createChannels;
|
||||||
int createWidth;
|
int createWidth;
|
||||||
int createHeight;
|
int createHeight;
|
||||||
@@ -75,6 +76,7 @@ namespace sharp {
|
|||||||
rawHeight(0),
|
rawHeight(0),
|
||||||
pages(1),
|
pages(1),
|
||||||
page(0),
|
page(0),
|
||||||
|
level(0),
|
||||||
createChannels(0),
|
createChannels(0),
|
||||||
createWidth(0),
|
createWidth(0),
|
||||||
createHeight(0),
|
createHeight(0),
|
||||||
|
|||||||
@@ -74,6 +74,15 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|||||||
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
|
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
|
||||||
baton->pagePrimary = image.get_int("heif-primary");
|
baton->pagePrimary = image.get_int("heif-primary");
|
||||||
}
|
}
|
||||||
|
if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
|
||||||
|
int const levels = std::stoi(image.get_string("openslide.level-count"));
|
||||||
|
for (int l = 0; l < levels; l++) {
|
||||||
|
std::string prefix = "openslide.level[" + std::to_string(l) + "].";
|
||||||
|
int const width = std::stoi(image.get_string((prefix + "width").data()));
|
||||||
|
int const height = std::stoi(image.get_string((prefix + "height").data()));
|
||||||
|
baton->levels.push_back(std::pair<int, int>(width, height));
|
||||||
|
}
|
||||||
|
}
|
||||||
baton->hasProfile = sharp::HasProfile(image);
|
baton->hasProfile = sharp::HasProfile(image);
|
||||||
// Derived attributes
|
// Derived attributes
|
||||||
baton->hasAlpha = sharp::HasAlpha(image);
|
baton->hasAlpha = sharp::HasAlpha(image);
|
||||||
@@ -177,6 +186,17 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|||||||
if (baton->pagePrimary > -1) {
|
if (baton->pagePrimary > -1) {
|
||||||
info.Set("pagePrimary", baton->pagePrimary);
|
info.Set("pagePrimary", baton->pagePrimary);
|
||||||
}
|
}
|
||||||
|
if (!baton->levels.empty()) {
|
||||||
|
int i = 0;
|
||||||
|
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
|
||||||
|
for (std::pair<int, int> const l : baton->levels) {
|
||||||
|
Napi::Object level = Napi::Object::New(env);
|
||||||
|
level.Set("width", l.first);
|
||||||
|
level.Set("height", l.second);
|
||||||
|
levels.Set(i++, level);
|
||||||
|
}
|
||||||
|
info.Set("levels", levels);
|
||||||
|
}
|
||||||
info.Set("hasProfile", baton->hasProfile);
|
info.Set("hasProfile", baton->hasProfile);
|
||||||
info.Set("hasAlpha", baton->hasAlpha);
|
info.Set("hasAlpha", baton->hasAlpha);
|
||||||
if (baton->orientation > 0) {
|
if (baton->orientation > 0) {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ struct MetadataBaton {
|
|||||||
int loop;
|
int loop;
|
||||||
std::vector<int> delay;
|
std::vector<int> delay;
|
||||||
int pagePrimary;
|
int pagePrimary;
|
||||||
|
std::vector<std::pair<int, int>> levels;
|
||||||
bool hasProfile;
|
bool hasProfile;
|
||||||
bool hasAlpha;
|
bool hasAlpha;
|
||||||
int orientation;
|
int orientation;
|
||||||
|
|||||||
@@ -645,8 +645,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Extract an image channel (aka vips band)
|
// Extract an image channel (aka vips band)
|
||||||
if (baton->extractChannel > -1) {
|
if (baton->extractChannel > -1) {
|
||||||
if (baton->extractChannel >= image.bands()) {
|
if (baton->extractChannel >= image.bands()) {
|
||||||
(baton->err).append("Cannot extract channel from image. Too few channels in image.");
|
if (baton->extractChannel == 3 && sharp::HasAlpha(image)) {
|
||||||
return Error();
|
baton->extractChannel = image.bands() - 1;
|
||||||
|
} else {
|
||||||
|
(baton->err).append("Cannot extract channel from image. Too few channels in image.");
|
||||||
|
return Error();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
VipsInterpretation const interpretation = sharp::Is16Bit(image.interpretation())
|
VipsInterpretation const interpretation = sharp::Is16Bit(image.interpretation())
|
||||||
? VIPS_INTERPRETATION_GREY16
|
? VIPS_INTERPRETATION_GREY16
|
||||||
|
|||||||
12
src/stats.cc
12
src/stats.cc
@@ -75,8 +75,17 @@ class StatsWorker : public Napi::AsyncWorker {
|
|||||||
baton->isOpaque = false;
|
baton->isOpaque = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Convert to greyscale
|
||||||
|
vips::VImage greyscale = image.colourspace(VIPS_INTERPRETATION_B_W)[0];
|
||||||
// Estimate entropy via histogram of greyscale value frequency
|
// Estimate entropy via histogram of greyscale value frequency
|
||||||
baton->entropy = std::abs(image.colourspace(VIPS_INTERPRETATION_B_W)[0].hist_find().hist_entropy());
|
baton->entropy = std::abs(greyscale.hist_find().hist_entropy());
|
||||||
|
// Estimate sharpness via standard deviation of greyscale laplacian
|
||||||
|
VImage laplacian = VImage::new_matrixv(3, 3,
|
||||||
|
0.0, 1.0, 0.0,
|
||||||
|
1.0, -4.0, 1.0,
|
||||||
|
0.0, 1.0, 0.0);
|
||||||
|
laplacian.set("scale", 9.0);
|
||||||
|
baton->sharpness = greyscale.conv(laplacian).deviate();
|
||||||
} catch (vips::VError const &err) {
|
} catch (vips::VError const &err) {
|
||||||
(baton->err).append(err.what());
|
(baton->err).append(err.what());
|
||||||
}
|
}
|
||||||
@@ -123,6 +132,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
|||||||
info.Set("channels", channels);
|
info.Set("channels", channels);
|
||||||
info.Set("isOpaque", baton->isOpaque);
|
info.Set("isOpaque", baton->isOpaque);
|
||||||
info.Set("entropy", baton->entropy);
|
info.Set("entropy", baton->entropy);
|
||||||
|
info.Set("sharpness", baton->sharpness);
|
||||||
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
|
||||||
} else {
|
} else {
|
||||||
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
|
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
|
||||||
|
|||||||
@@ -47,13 +47,15 @@ struct StatsBaton {
|
|||||||
std::vector<ChannelStats> channelStats;
|
std::vector<ChannelStats> channelStats;
|
||||||
bool isOpaque;
|
bool isOpaque;
|
||||||
double entropy;
|
double entropy;
|
||||||
|
double sharpness;
|
||||||
|
|
||||||
std::string err;
|
std::string err;
|
||||||
|
|
||||||
StatsBaton():
|
StatsBaton():
|
||||||
input(nullptr),
|
input(nullptr),
|
||||||
isOpaque(true),
|
isOpaque(true),
|
||||||
entropy(0.0)
|
entropy(0.0),
|
||||||
|
sharpness(0.0)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
BIN
test/fixtures/expected/extract-alpha-2-channel.png
vendored
Normal file
BIN
test/fixtures/expected/extract-alpha-2-channel.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
@@ -80,6 +80,19 @@ describe('Image channel extraction', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Alpha from 2-channel input', function (done) {
|
||||||
|
const output = fixtures.path('output.extract-alpha-2-channel.png');
|
||||||
|
sharp(fixtures.inputPngWithGreyAlpha)
|
||||||
|
.extractChannel('alpha')
|
||||||
|
.toColourspace('b-w')
|
||||||
|
.toFile(output, function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(1, info.channels);
|
||||||
|
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-2-channel.png'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Invalid channel number', function () {
|
it('Invalid channel number', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
|
|||||||
@@ -19,11 +19,17 @@ describe('failOnError', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles truncated PNG', function (done) {
|
it('handles truncated PNG, emits warnings', function (done) {
|
||||||
|
let isWarningEmitted = false;
|
||||||
sharp(fixtures.inputPngTruncated, { failOnError: false })
|
sharp(fixtures.inputPngTruncated, { failOnError: false })
|
||||||
|
.on('warning', function (warning) {
|
||||||
|
assert.strictEqual('not enough data', warning);
|
||||||
|
isWarningEmitted = true;
|
||||||
|
})
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, isWarningEmitted);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Fail when input is empty Buffer', function (done) {
|
it('Fail when input is empty Buffer', function (done) {
|
||||||
if (process.platform === 'freebsd') return this.skip(); // can be removed with libvips 8.10.0+
|
if (sharp.format.magick.input.buffer) return this.skip(); // can be removed with libvips 8.10.0+
|
||||||
sharp(Buffer.alloc(0)).toBuffer().then(function () {
|
sharp(Buffer.alloc(0)).toBuffer().then(function () {
|
||||||
assert(false);
|
assert(false);
|
||||||
done();
|
done();
|
||||||
@@ -659,6 +659,19 @@ describe('Input/output', function () {
|
|||||||
sharp({ pages: '1' });
|
sharp({ pages: '1' });
|
||||||
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
|
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
|
||||||
});
|
});
|
||||||
|
it('Valid level property', function () {
|
||||||
|
sharp({ level: 1 });
|
||||||
|
});
|
||||||
|
it('Invalid level property (string) throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ level: '1' });
|
||||||
|
}, /Expected integer between 0 and 256 for level but received 1 of type string/);
|
||||||
|
});
|
||||||
|
it('Invalid level property (negative) throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ level: -1 });
|
||||||
|
}, /Expected integer between 0 and 256 for level but received -1 of type number/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create new image', function () {
|
describe('create new image', function () {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual('srgb', metadata.space);
|
assert.strictEqual('srgb', metadata.space);
|
||||||
assert.strictEqual(3, metadata.channels);
|
assert.strictEqual(3, metadata.channels);
|
||||||
assert.strictEqual('uchar', metadata.depth);
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
assert.strictEqual('undefined', typeof metadata.density);
|
assert.strictEqual(true, ['undefined', 'number'].includes(typeof metadata.density));
|
||||||
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
||||||
assert.strictEqual(false, metadata.isProgressive);
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
@@ -311,7 +311,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual('srgb', metadata.space);
|
assert.strictEqual('srgb', metadata.space);
|
||||||
assert.strictEqual(3, metadata.channels);
|
assert.strictEqual(3, metadata.channels);
|
||||||
assert.strictEqual('uchar', metadata.depth);
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
assert.strictEqual('undefined', typeof metadata.density);
|
assert.strictEqual(true, ['undefined', 'number'].includes(typeof metadata.density));
|
||||||
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
||||||
assert.strictEqual(false, metadata.isProgressive);
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
@@ -343,7 +343,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual('srgb', metadata.space);
|
assert.strictEqual('srgb', metadata.space);
|
||||||
assert.strictEqual(3, metadata.channels);
|
assert.strictEqual(3, metadata.channels);
|
||||||
assert.strictEqual('uchar', metadata.depth);
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
assert.strictEqual('undefined', typeof metadata.density);
|
assert.strictEqual(true, ['undefined', 'number'].includes(typeof metadata.density));
|
||||||
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
||||||
assert.strictEqual(false, metadata.isProgressive);
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
@@ -381,7 +381,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual('srgb', metadata.space);
|
assert.strictEqual('srgb', metadata.space);
|
||||||
assert.strictEqual(3, metadata.channels);
|
assert.strictEqual(3, metadata.channels);
|
||||||
assert.strictEqual('uchar', metadata.depth);
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
assert.strictEqual('undefined', typeof metadata.density);
|
assert.strictEqual(true, ['undefined', 'number'].includes(typeof metadata.density));
|
||||||
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
||||||
assert.strictEqual(false, metadata.isProgressive);
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
@@ -405,7 +405,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual('srgb', metadata.space);
|
assert.strictEqual('srgb', metadata.space);
|
||||||
assert.strictEqual(3, metadata.channels);
|
assert.strictEqual(3, metadata.channels);
|
||||||
assert.strictEqual('uchar', metadata.depth);
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
assert.strictEqual('undefined', typeof metadata.density);
|
assert.strictEqual(true, ['undefined', 'number'].includes(typeof metadata.density));
|
||||||
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
assert.strictEqual('4:2:0', metadata.chromaSubsampling);
|
||||||
assert.strictEqual(false, metadata.isProgressive);
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ describe('PNG', function () {
|
|||||||
it('Valid PNG libimagequant quality value produces image of same size or smaller', function () {
|
it('Valid PNG libimagequant quality value produces image of same size or smaller', function () {
|
||||||
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
sharp(inputPngBuffer).resize(10).png({ palette: true, quality: 80 }).toBuffer(),
|
sharp(inputPngBuffer).resize(10).png({ quality: 80 }).toBuffer(),
|
||||||
sharp(inputPngBuffer).resize(10).png({ palette: true, quality: 100 }).toBuffer()
|
sharp(inputPngBuffer).resize(10).png({ quality: 100 }).toBuffer()
|
||||||
]).then(function (data) {
|
]).then(function (data) {
|
||||||
assert.strictEqual(true, data[0].length <= data[1].length);
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
});
|
});
|
||||||
@@ -136,15 +136,15 @@ describe('PNG', function () {
|
|||||||
|
|
||||||
it('Invalid PNG libimagequant quality value throws error', function () {
|
it('Invalid PNG libimagequant quality value throws error', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().png({ palette: true, quality: 101 });
|
sharp().png({ quality: 101 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Valid PNG libimagequant colours value produces image of same size or smaller', function () {
|
it('Valid PNG libimagequant colours value produces image of same size or smaller', function () {
|
||||||
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
sharp(inputPngBuffer).resize(10).png({ palette: true, colours: 100 }).toBuffer(),
|
sharp(inputPngBuffer).resize(10).png({ colours: 100 }).toBuffer(),
|
||||||
sharp(inputPngBuffer).resize(10).png({ palette: true, colours: 200 }).toBuffer()
|
sharp(inputPngBuffer).resize(10).png({ colours: 200 }).toBuffer()
|
||||||
]).then(function (data) {
|
]).then(function (data) {
|
||||||
assert.strictEqual(true, data[0].length <= data[1].length);
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
});
|
});
|
||||||
@@ -152,21 +152,21 @@ describe('PNG', function () {
|
|||||||
|
|
||||||
it('Invalid PNG libimagequant colours value throws error', function () {
|
it('Invalid PNG libimagequant colours value throws error', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().png({ palette: true, colours: -1 });
|
sharp().png({ colours: -1 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Invalid PNG libimagequant colors value throws error', function () {
|
it('Invalid PNG libimagequant colors value throws error', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().png({ palette: true, colors: 0.1 });
|
sharp().png({ colors: 0.1 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Valid PNG libimagequant dither value produces image of same size or smaller', function () {
|
it('Valid PNG libimagequant dither value produces image of same size or smaller', function () {
|
||||||
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
sharp(inputPngBuffer).resize(10).png({ palette: true, dither: 0.1 }).toBuffer(),
|
sharp(inputPngBuffer).resize(10).png({ dither: 0.1 }).toBuffer(),
|
||||||
sharp(inputPngBuffer).resize(10).png({ palette: true, dither: 0.9 }).toBuffer()
|
sharp(inputPngBuffer).resize(10).png({ dither: 0.9 }).toBuffer()
|
||||||
]).then(function (data) {
|
]).then(function (data) {
|
||||||
assert.strictEqual(true, data[0].length <= data[1].length);
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
});
|
});
|
||||||
@@ -174,7 +174,7 @@ describe('PNG', function () {
|
|||||||
|
|
||||||
it('Invalid PNG libimagequant dither value throws error', function () {
|
it('Invalid PNG libimagequant dither value throws error', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().png({ palette: true, dither: 'fail' });
|
sharp().png({ dither: 'fail' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 0.7883011147075762));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -84,6 +85,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3409031108021736));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3409031108021736));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 9.111356137722868));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -110,6 +112,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(false, stats.isOpaque);
|
assert.strictEqual(false, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.06778064835816622));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.06778064835816622));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 2.522916068931278));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -185,6 +188,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(false, stats.isOpaque);
|
assert.strictEqual(false, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 0));
|
||||||
|
|
||||||
// alpha channel
|
// alpha channel
|
||||||
assert.strictEqual(0, stats.channels[3].min);
|
assert.strictEqual(0, stats.channels[3].min);
|
||||||
@@ -212,6 +216,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3851250782608986));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3851250782608986));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 10.312521863719589));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -239,6 +244,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.51758075132966));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.51758075132966));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 9.959951636662941));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -298,6 +304,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 6.087309412541799));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 6.087309412541799));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 2.9250574456255682));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(35, stats.channels[0].min);
|
assert.strictEqual(35, stats.channels[0].min);
|
||||||
@@ -357,6 +364,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(false, stats.isOpaque);
|
assert.strictEqual(false, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 1));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 1));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 15.870619016486861));
|
||||||
|
|
||||||
// gray channel
|
// gray channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -410,6 +418,7 @@ describe('Image Stats', function () {
|
|||||||
|
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 0.788301114707569));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -472,6 +481,7 @@ describe('Image Stats', function () {
|
|||||||
return pipeline.stats().then(function (stats) {
|
return pipeline.stats().then(function (stats) {
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 0.788301114707569));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -529,6 +539,7 @@ describe('Image Stats', function () {
|
|||||||
return sharp(fixtures.inputJpg).stats().then(function (stats) {
|
return sharp(fixtures.inputJpg).stats().then(function (stats) {
|
||||||
assert.strictEqual(true, stats.isOpaque);
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(stats.sharpness, 0.788301114707569));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
@@ -582,6 +593,18 @@ describe('Image Stats', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Blurred image has lower sharpness than original', () => {
|
||||||
|
const original = sharp(fixtures.inputJpg).stats();
|
||||||
|
const blurred = sharp(fixtures.inputJpg).blur().toBuffer().then(blur => sharp(blur).stats());
|
||||||
|
|
||||||
|
return Promise
|
||||||
|
.all([original, blurred])
|
||||||
|
.then(([original, blurred]) => {
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(original.sharpness, 0.7883011147075476));
|
||||||
|
assert.strictEqual(true, isInAcceptableRange(blurred.sharpness, 0.4791559805997398));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
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)
|
||||||
.stats(function (err) {
|
.stats(function (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user