Compare commits

...

21 Commits

Author SHA1 Message Date
Lovell Fuller
df6efa0285 Release v0.25.4 2020-06-12 14:17:23 +01:00
Lovell Fuller
3c10e118e3 Docs: ensure presence of constructor description 2020-06-12 13:52:19 +01:00
Lovell Fuller
19980190f7 Emit 'warning' event for non-critical problems #2032 2020-06-12 13:46:12 +01:00
Lovell Fuller
8f5495a446 Add experimental sharpness calc to stats #2251 2020-06-12 11:25:57 +01:00
Mikhail Bodrov
9431029917 Simplify type checking of plainObject, buffer (#2252) 2020-06-10 20:54:01 +01:00
Lovell Fuller
17ea70a102 Add named 'alpha' channel to extractChannel op #2138 2020-06-07 10:43:43 +01:00
Lovell Fuller
7f142bddb3 Add level constructor opt for multi-level input #2222 2020-06-06 16:10:56 +01:00
Lovell Fuller
98e0516ac1 Tests: latest libvips sets JPEG density 2020-06-06 16:09:52 +01:00
Mikhail Bodrov
7717516d1d Simplify is helpers (#2244) 2020-06-05 09:48:26 +01:00
Lovell Fuller
760550ca0d Expose levels metadata for multi-level images #2222 2020-06-04 20:29:02 +01:00
Lovell Fuller
f8144dd89c Bump dependencies 2020-06-04 20:17:15 +01:00
Lovell Fuller
ac4070cb49 Tests: skip empty Buffer test when *magick supported 2020-06-04 20:07:30 +01:00
Lovell Fuller
25b964e3df Docs: changelog and credit update for #2226 2020-05-28 22:42:37 +01:00
Lovell Fuller
17ec6c72df Docs: inline small external docute CSS/JS 2020-05-28 22:41:24 +01:00
Roman Malieiev
a7b1185602 Enable PNG palette when at least one of quality, colours, colors or dither is set (#2226) 2020-05-28 22:21:33 +01:00
Lovell Fuller
c76ae06fd1 Attempt to reduce file size of binaries at link time 2020-05-22 19:47:19 +01:00
Lovell Fuller
b3dd54d550 Changelog and doc update for #2217 2020-05-22 16:48:27 +01:00
malice00
d248eadb06 Allow libvips binary location override with version appended (#2217) 2020-05-22 16:07:32 +01:00
Lovell Fuller
507eef3053 Docs: reorder/highlight optional output properties 2020-05-19 18:55:33 +01:00
Lovell Fuller
fc2f672337 Improve error messaging when platform mismatch detected 2020-05-19 18:53:39 +01:00
Lovell Fuller
6f49be8f26 Docs: small perf boost by prefetching image DNS 2020-05-19 18:50:18 +01:00
32 changed files with 369 additions and 145 deletions

View File

@@ -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': [

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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',

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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>}
*/ */

View File

@@ -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);
}; };
/** /**

View File

@@ -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;

View File

@@ -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);
} }
} }
} }

View File

@@ -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",

View File

@@ -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);

View File

@@ -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),

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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

View File

@@ -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() });

View File

@@ -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)
{} {}
}; };

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -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)

View File

@@ -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);

View File

@@ -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 () {

View File

@@ -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);

View File

@@ -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' });
}); });
}); });
}); });

View File

@@ -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) {