Compare commits

..

17 Commits

Author SHA1 Message Date
Lovell Fuller
8a3b660bbc Release v0.15.0 2016-05-21 15:33:56 +01:00
Lovell Fuller
933989c87d Update benchmark results ahead of v0.15.0, ~20% improvement 2016-05-21 10:21:42 +01:00
Lovell Fuller
e3cbcb98c0 Add hints about compiling with _GLIBCXX_USE_CXX11_ABI #432
Increase deprecatedness of preinstall script
Additional valgrind suppressions for libwebp
2016-05-21 09:03:09 +01:00
Lovell Fuller
32a2787254 Thank you to all the new contributors 2016-05-18 20:40:31 +01:00
Lovell Fuller
fccfc27de0 Update ARM packaging to libvips v8.3.1 2016-05-18 19:58:30 +01:00
Lovell Fuller
cdb2894bd9 Use libvips' new lanczos3 kernel as default for image reduce
Deprecate interpolateWith method, now provided as an option
2016-05-18 19:57:22 +01:00
Lovell Fuller
051d022fc2 Upgrade to libvips v8.3.1
Remove packaging tests and therefore support for Centos 6
2016-05-08 22:15:08 +01:00
Lovell Fuller
7388d97502 Allow keyword macros for glib support on MSVC 2016-05-08 13:30:42 +01:00
Lovell Fuller
1bece3a792 Add 2 channel (grey+alpha) GIF test case #375 2016-05-07 20:04:17 +01:00
Lovell Fuller
1de0038516 Upgrade to libvips 8.3.x
Add support for libvips' new native loaders, including GIF and SVG
Pre-built binaries now include giflib and librsvg, exclude *magick
2016-05-07 20:04:17 +01:00
Lovell Fuller
b7a098fb28 Break existing sharpen API to accept sigma and improve precision 2016-05-07 20:04:17 +01:00
Lovell Fuller
ee21d2991c Use shrink-on-load for WebP input 2016-05-07 20:04:17 +01:00
Lovell Fuller
f8eab49962 Add Node v6 to CI builds 2016-05-07 19:50:15 +01:00
Lovell Fuller
c9b3847a69 Docs: basic security considerations for installation #424 2016-05-07 19:48:06 +01:00
Felix Bünemann
dce3840537 Update Lambda instructions for Node.js 4.3 (#419)
Amazon introduced Node.js 4.3 support for Lambda, which is now the
recommended runtime instead of the old Node.js 0.10. This commit revises
the Lambda docs to build Node.js 4.3 compatible binaries using the
latest stable Node.js 4.x packages from Nodesource.
2016-04-27 19:39:30 +01:00
Lovell Fuller
b6030c161b Update expected test fixtures for libvips 8.3 2016-04-23 20:07:55 +01:00
Lovell Fuller
c920180cb3 Remove (un)premultiply ops when not resizing/compositing #413 2016-04-23 19:50:00 +01:00
58 changed files with 1520 additions and 1025 deletions

View File

@@ -4,6 +4,7 @@ node_js:
- "0.12" - "0.12"
- "4" - "4"
- "5" - "5"
- "6"
os: os:
- linux - linux
- osx - osx

View File

@@ -41,7 +41,6 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch | | Release | WIP branch |
| ------: | :--------- | | ------: | :--------- |
| v0.15.0 | outfit |
| v0.16.0 | pencil | | v0.16.0 | pencil |
| v0.17.0 | quill | | v0.17.0 | quill |

View File

@@ -1,14 +1,14 @@
# sharp # sharp
The typical use case for this high speed Node.js module The typical use case for this high speed Node.js module
is to convert large images of many formats to is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions. smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x faster than using the Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings. quickest ImageMagick and GraphicsMagick settings.
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly. Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not sacrificed for speed. Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available. rotation, extraction, compositing and gamma correction are available.

View File

@@ -8,6 +8,7 @@ environment:
- nodejs_version: "0.12" - nodejs_version: "0.12"
- nodejs_version: "4" - nodejs_version: "4"
- nodejs_version: "5" - nodejs_version: "5"
- nodejs_version: "6"
install: install:
- ps: Install-Product node $env:nodejs_version x64 - ps: Install-Product node $env:nodejs_version x64
- npm install - npm install

View File

@@ -9,7 +9,8 @@
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")' 'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
}, },
'defines': [ 'defines': [
'VIPS_CPLUSPLUS_EXPORTS' 'VIPS_CPLUSPLUS_EXPORTS',
'_ALLOW_KEYWORD_MACROS'
], ],
'sources': [ 'sources': [
'src/libvips/cplusplus/VError.cpp', 'src/libvips/cplusplus/VError.cpp',
@@ -91,7 +92,8 @@
'src/utilities.cc' 'src/utilities.cc'
], ],
'defines': [ 'defines': [
'_GLIBCXX_USE_CXX11_ABI=0' '_GLIBCXX_USE_CXX11_ABI=0',
'_ALLOW_KEYWORD_MACROS'
], ],
'include_dirs': [ 'include_dirs': [
'<!(node -e "require(\'nan\')")' '<!(node -e "require(\'nan\')")'
@@ -132,23 +134,33 @@
'<(module_root_dir)/lib/libglib-2.0.so', '<(module_root_dir)/lib/libglib-2.0.so',
'<(module_root_dir)/lib/libgobject-2.0.so', '<(module_root_dir)/lib/libgobject-2.0.so',
# Dependencies of dependencies, included for openSUSE support # Dependencies of dependencies, included for openSUSE support
'<(module_root_dir)/lib/libGraphicsMagick.so', '<(module_root_dir)/lib/libcairo.so',
'<(module_root_dir)/lib/libGraphicsMagickWand.so', '<(module_root_dir)/lib/libcroco-0.6.so',
'<(module_root_dir)/lib/libexif.so', '<(module_root_dir)/lib/libexif.so',
'<(module_root_dir)/lib/libffi.so',
'<(module_root_dir)/lib/libfontconfig.so',
'<(module_root_dir)/lib/libfreetype.so',
'<(module_root_dir)/lib/libgdk_pixbuf-2.0.so',
'<(module_root_dir)/lib/libgif.so',
'<(module_root_dir)/lib/libgio-2.0.so', '<(module_root_dir)/lib/libgio-2.0.so',
'<(module_root_dir)/lib/libgmodule-2.0.so', '<(module_root_dir)/lib/libgmodule-2.0.so',
'<(module_root_dir)/lib/libgsf-1.so', '<(module_root_dir)/lib/libgsf-1.so',
'<(module_root_dir)/lib/libgthread-2.0.so',
'<(module_root_dir)/lib/libharfbuzz.so',
'<(module_root_dir)/lib/libjpeg.so', '<(module_root_dir)/lib/libjpeg.so',
'<(module_root_dir)/lib/liblcms2.so',
'<(module_root_dir)/lib/liborc-0.4.so',
'<(module_root_dir)/lib/libpango-1.0.so',
'<(module_root_dir)/lib/libpangocairo-1.0.so',
'<(module_root_dir)/lib/libpangoft2-1.0.so',
'<(module_root_dir)/lib/libpixman-1.so',
'<(module_root_dir)/lib/libpng.so', '<(module_root_dir)/lib/libpng.so',
'<(module_root_dir)/lib/libpng16.so',
'<(module_root_dir)/lib/librsvg-2.so',
'<(module_root_dir)/lib/libtiff.so', '<(module_root_dir)/lib/libtiff.so',
'<(module_root_dir)/lib/libwebp.so', '<(module_root_dir)/lib/libwebp.so',
'<(module_root_dir)/lib/libz.so',
'<(module_root_dir)/lib/libffi.so',
'<(module_root_dir)/lib/libgthread-2.0.so',
'<(module_root_dir)/lib/liblcms2.so',
'<(module_root_dir)/lib/libpng16.so',
'<(module_root_dir)/lib/libxml2.so', '<(module_root_dir)/lib/libxml2.so',
'<(module_root_dir)/lib/liborc-0.4.so', '<(module_root_dir)/lib/libz.so',
# Ensure runtime linking is relative to sharp.node # Ensure runtime linking is relative to sharp.node
'-Wl,-rpath=\'$${ORIGIN}/../../lib\'' '-Wl,-rpath=\'$${ORIGIN}/../../lib\''
] ]
@@ -199,12 +211,11 @@
'destination': '<(module_root_dir)/build/Release', 'destination': '<(module_root_dir)/build/Release',
'files': [ 'files': [
'<(module_root_dir)/lib/GNU.Gettext.dll', '<(module_root_dir)/lib/GNU.Gettext.dll',
'<(module_root_dir)/lib/libMagickCore-6.Q16-2.dll',
'<(module_root_dir)/lib/libMagickWand-6.Q16-2.dll',
'<(module_root_dir)/lib/libasprintf-0.dll', '<(module_root_dir)/lib/libasprintf-0.dll',
'<(module_root_dir)/lib/libcairo-2.dll', '<(module_root_dir)/lib/libcairo-2.dll',
'<(module_root_dir)/lib/libcairo-gobject-2.dll', '<(module_root_dir)/lib/libcairo-gobject-2.dll',
'<(module_root_dir)/lib/libcairo-script-interpreter-2.dll', '<(module_root_dir)/lib/libcairo-script-interpreter-2.dll',
'<(module_root_dir)/lib/libcroco-0.6-3.dll',
'<(module_root_dir)/lib/libexif-12.dll', '<(module_root_dir)/lib/libexif-12.dll',
'<(module_root_dir)/lib/libexpat-1.dll', '<(module_root_dir)/lib/libexpat-1.dll',
'<(module_root_dir)/lib/libffi-6.dll', '<(module_root_dir)/lib/libffi-6.dll',
@@ -213,6 +224,7 @@
'<(module_root_dir)/lib/libfreetype-6.dll', '<(module_root_dir)/lib/libfreetype-6.dll',
'<(module_root_dir)/lib/libgcc_s_seh-1.dll', '<(module_root_dir)/lib/libgcc_s_seh-1.dll',
'<(module_root_dir)/lib/libgdk_pixbuf-2.0-0.dll', '<(module_root_dir)/lib/libgdk_pixbuf-2.0-0.dll',
'<(module_root_dir)/lib/libgif-4.dll',
'<(module_root_dir)/lib/libgio-2.0-0.dll', '<(module_root_dir)/lib/libgio-2.0-0.dll',
'<(module_root_dir)/lib/libglib-2.0-0.dll', '<(module_root_dir)/lib/libglib-2.0-0.dll',
'<(module_root_dir)/lib/libgmodule-2.0-0.dll', '<(module_root_dir)/lib/libgmodule-2.0-0.dll',
@@ -222,18 +234,18 @@
'<(module_root_dir)/lib/libintl-8.dll', '<(module_root_dir)/lib/libintl-8.dll',
'<(module_root_dir)/lib/libjpeg-62.dll', '<(module_root_dir)/lib/libjpeg-62.dll',
'<(module_root_dir)/lib/liblcms2-2.dll', '<(module_root_dir)/lib/liblcms2-2.dll',
'<(module_root_dir)/lib/libopenjp2.dll',
'<(module_root_dir)/lib/libopenslide-0.dll',
'<(module_root_dir)/lib/libpango-1.0-0.dll', '<(module_root_dir)/lib/libpango-1.0-0.dll',
'<(module_root_dir)/lib/libpangocairo-1.0-0.dll', '<(module_root_dir)/lib/libpangocairo-1.0-0.dll',
'<(module_root_dir)/lib/libpangowin32-1.0-0.dll', '<(module_root_dir)/lib/libpangowin32-1.0-0.dll',
'<(module_root_dir)/lib/libpixman-1-0.dll', '<(module_root_dir)/lib/libpixman-1-0.dll',
'<(module_root_dir)/lib/libpng16-16.dll', '<(module_root_dir)/lib/libpng16-16.dll',
'<(module_root_dir)/lib/libquadmath-0.dll', '<(module_root_dir)/lib/libquadmath-0.dll',
'<(module_root_dir)/lib/libsqlite3-0.dll', '<(module_root_dir)/lib/librsvg-2-2.dll',
'<(module_root_dir)/lib/libssp-0.dll', '<(module_root_dir)/lib/libssp-0.dll',
'<(module_root_dir)/lib/libstdc++-6.dll',
'<(module_root_dir)/lib/libtiff-5.dll', '<(module_root_dir)/lib/libtiff-5.dll',
'<(module_root_dir)/lib/libvips-42.dll', '<(module_root_dir)/lib/libvips-42.dll',
'<(module_root_dir)/lib/libwebp-6.dll',
'<(module_root_dir)/lib/libxml2-2.dll', '<(module_root_dir)/lib/libxml2-2.dll',
'<(module_root_dir)/lib/zlib1.dll' '<(module_root_dir)/lib/zlib1.dll'
] ]

View File

@@ -13,7 +13,7 @@ Constructor to which further methods are chained.
`input`, if present, can be one of: `input`, if present, can be one of:
* Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or * Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* String containing the path to an image file, with most major formats supported. * String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data
can be streamed into the object when `input` is `null` or `undefined`. can be streamed into the object when `input` is `null` or `undefined`.
@@ -57,7 +57,7 @@ Fast access to image metadata without decoding any compressed image data.
`callback`, if present, gets the arguments `(err, metadata)` where `metadata` has the attributes: `callback`, if present, gets the arguments `(err, metadata)` where `metadata` has the attributes:
* `format`: Name of decoder to be used to decompress image data e.g. `jpeg`, `png`, `webp` (for file-based input additionally `tiff`, `magick`, `openslide`, `ppm`, `fits`) * `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
* `width`: Number of pixels wide * `width`: Number of pixels wide
* `height`: Number of pixels high * `height`: Number of pixels high
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L522) * `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L522)
@@ -118,7 +118,7 @@ Do not process input images where the number of pixels (width * height) exceeds
### Resizing ### Resizing
#### resize([width], [height]) #### resize([width], [height], [options])
Scale output to `width` x `height`. By default, the resized image is cropped to the exact size specified. Scale output to `width` x `height`. By default, the resized image is cropped to the exact size specified.
@@ -126,6 +126,42 @@ Scale output to `width` x `height`. By default, the resized image is cropped to
`height` is the integral Number of pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width. `height` is the integral Number of pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width.
`options` is an optional Object. If present, it can contain one or more of:
* `options.kernel`, the kernel to use for image reduction, defaulting to `lanczos3`.
* `options.interpolator`, the interpolator to use for image enlargement, defaulting to `bicubic`.
Possible kernels are:
* `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
* `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
* `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
Possible interpolators are:
* `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
* `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), faster than bicubic but with less smooth results.
* `vertexSplitQuadraticBasisSpline`: Use the smoother [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48) to prevent "staircasing" when enlarging.
* `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default).
* `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2.
* `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance but typically reduces performance by a factor of 3.
```javascript
sharp(inputBuffer)
.resize(200, 300, {
kernel: sharp.kernel.lanczos2,
interpolator: sharp.interpolator.nohalo
})
.background('white')
.embed()
.toFile('output.tiff')
.then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a lanczos2/nohalo scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
#### crop([option]) #### crop([option])
Crop the resized image to the exact size specified, the default behaviour. Crop the resized image to the exact size specified, the default behaviour.
@@ -232,37 +268,6 @@ if its width or height exceeds the geometry specification*".
Ignoring the aspect ratio of the input, stretch the image to the exact `width` and/or `height` provided via `resize`. Ignoring the aspect ratio of the input, stretch the image to the exact `width` and/or `height` provided via `resize`.
#### interpolateWith(interpolator)
Use the given interpolator for image resizing, where `interpolator` is an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.bicubic`.
The default interpolator is `bicubic`, providing a general-purpose interpolator that is both fast and of good quality.
Possible interpolators, in order of performance, are:
* `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation), suitable for image enlargement only.
* `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), faster than bicubic but with less smooth results.
* `vertexSplitQuadraticBasisSpline`: Use the smoother [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48) to prevent "staircasing" when enlarging.
* `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default).
* `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2.
* `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance but typically reduces performance by a factor of 3.
[Compare the output of these interpolators](https://github.com/lovell/sharp/tree/master/test/interpolators)
```javascript
sharp(inputBuffer)
.resize(200, 300)
.interpolateWith(sharp.interpolator.nohalo)
.background('white')
.embed()
.toFile('output.tiff')
.then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a nohalo scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
### Operations ### Operations
#### extract({ left: left, top: top, width: width, height: height }) #### extract({ left: left, top: top, width: width, height: height })
@@ -374,15 +379,15 @@ When used without parameters, performs a fast, mild blur of the output image. Th
When a `sigma` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 25%. When a `sigma` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 25%.
* `sigma`, if present, is a Number between 0.3 and 1000 representing the approximate blur radius in pixels. * `sigma`, if present, is a Number between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
#### sharpen([radius], [flat], [jagged]) #### sharpen([sigma], [flat], [jagged])
When used without parameters, performs a fast, mild sharpen of the output image. This typically reduces performance by 10%. When used without parameters, performs a fast, mild sharpen of the output image. This typically reduces performance by 10%.
When a `radius` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space. Separate control over the level of sharpening in "flat" and "jagged" areas is available. This typically reduces performance by 50%. When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space. Separate control over the level of sharpening in "flat" and "jagged" areas is available. This typically reduces performance by 50%.
* `radius`, if present, is an integral Number representing the sharpen mask radius in pixels. * `sigma`, if present, is a Number representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0. * `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0.
* `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0. * `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0.
@@ -615,9 +620,6 @@ for example:
tiff: { id: 'tiff', tiff: { id: 'tiff',
input: { file: true, buffer: true, stream: true }, input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: false, stream: false } }, output: { file: true, buffer: false, stream: false } },
magick: { id: 'magick',
input: { file: true, buffer: true, stream: true },
output: { file: false, buffer: false, stream: false } },
raw: { id: 'raw', raw: { id: 'raw',
input: { file: false, buffer: false, stream: false }, input: { file: false, buffer: false, stream: false },
output: { file: false, buffer: true, stream: true } } } output: { file: false, buffer: true, stream: true } } }
@@ -641,22 +643,7 @@ sharp.queue.on('change', function(queueLength) {
An Object containing the version numbers of libvips and, on Linux, its dependencies. An Object containing the version numbers of libvips and, on Linux, its dependencies.
```javascript ```javascript
> console.log(sharp.versions); console.log(sharp.versions);
{ zlib: '1.2.8',
ffi: '3.2.1',
glib: '2.46.2',
xml: '2.9.2',
gsf: '1.14.34',
exif: '0.6.21',
jpeg: '1.4.2',
png: '1.6.19',
lcms: '2.7',
webp: '0.4.4',
tiff: '4.0.6',
magick: '6.9.2-6',
orc: '0.4.24',
vips: '8.1.1' }
``` ```
### Utilities ### Utilities

View File

@@ -1,5 +1,27 @@
# Changelog # Changelog
### v0.15 - "*outfit*"
Requires libvips v8.3.1
#### v0.15.0 - 21<sup>st</sup> May 2016
* Use libvips' new Lanczos 3 kernel as default for image reduction.
Deprecate interpolateWith method, now provided as a resize option.
[#310](https://github.com/lovell/sharp/issues/310)
[@jcupitt](https://github.com/jcupitt)
* Take advantage of libvips v8.3 features.
Add support for libvips' new GIF and SVG loaders.
Pre-built binaries now include giflib and librsvg, exclude *magick.
Use shrink-on-load for WebP input.
Break existing sharpen API to accept sigma and improve precision.
[#369](https://github.com/lovell/sharp/issues/369)
* Remove unnecessary (un)premultiply operations when not resizing/compositing.
[#413](https://github.com/lovell/sharp/issues/413)
[@jardakotesovec](https://github.com/jardakotesovec)
### v0.14 - "*needle*" ### v0.14 - "*needle*"
Requires libvips v8.2.3 Requires libvips v8.2.3

View File

@@ -1,14 +1,14 @@
# sharp # sharp
The typical use case for this high speed Node.js module The typical use case for this high speed Node.js module
is to convert large images of many formats to is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions. smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x faster than using the Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings. quickest ImageMagick and GraphicsMagick settings.
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly. Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not sacrificed for speed. Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available. rotation, extraction, compositing and gamma correction are available.
@@ -23,8 +23,7 @@ to install the libvips dependency.
### Formats ### Formats
This module supports reading JPEG, PNG, WebP, TIFF, OpenSlide, This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images.
GIF and most other libmagick-supported formats.
Output images can be in JPEG, PNG and WebP formats as well as uncompressed raw pixel data. Output images can be in JPEG, PNG and WebP formats as well as uncompressed raw pixel data.
@@ -89,12 +88,16 @@ the help and code contributions of the following people:
* [Alaric Holloway](https://github.com/skedastik) * [Alaric Holloway](https://github.com/skedastik)
* [Bernhard K. Weisshuhn](https://github.com/bkw) * [Bernhard K. Weisshuhn](https://github.com/bkw)
* [David A. Carley](https://github.com/dacarley) * [David A. Carley](https://github.com/dacarley)
* [John Tobin](https://github.com/jtobinisaniceguy)
* [Kenton Gray](https://github.com/kentongray)
* [Felix Bünemann](https://github.com/felixbuenemann)
* [Samy Al Zahrani](https://github.com/salzhrani)
Thank you! Thank you!
### Licence ### Licence
Copyright 2013, 2014, 2015 Lovell Fuller and contributors. Copyright 2013, 2014, 2015, 2016 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -6,7 +6,7 @@ npm install sharp
### Prerequisites ### Prerequisites
* C++11 compatible compiler such as gcc 4.6+ (Node v4+ requires gcc 4.8+), clang 3.0+ or MSVC 2013 * C++11 compatible compiler such as gcc 4.8+, clang 3.0+ or MSVC 2013+
* [node-gyp](https://github.com/TooTallNate/node-gyp#installation) * [node-gyp](https://github.com/TooTallNate/node-gyp#installation)
### Linux ### Linux
@@ -15,36 +15,38 @@ npm install sharp
[![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/lovell/sharp) [![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules/sharp/lib` during `npm install`. libvips and its dependencies are fetched and stored within `node_modules/sharp/lib` during `npm install`.
This involves an automated HTTPS download of approximately 6MB. This involves an automated HTTPS download of approximately 6.7MB.
Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ CPUs should "just work", e.g.: Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ CPUs should "just work", e.g.:
* Debian 7, 8 * Debian 7, 8
* Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10 * Ubuntu 12.04, 14.04, 15.10, 16.04
* Centos 7 * Centos 7
* Fedora 21, 22, 23 * Fedora 22, 23
* openSUSE 13.2 * openSUSE 13.2
* Archlinux 2015.06.01 * Archlinux 2015.06.01
* Raspbian Jessie * Raspbian Jessie
* Amazon Linux 2015.03, 2015.09 * Amazon Linux 2015.03, 2015.09
To use your own version of libvips instead of the provided binaries, make sure it is To use your own version of libvips instead of the provided binaries, make sure it is
at least the version listed under `config.libvips` in the `package.json` file that it at least the version listed under `config.libvips` in the `package.json` file,
can be located using `pkg-config`. If you are using non-stadard paths (anything other that it can be located using `pkg-config --modversion vips-cpp`
than `/usr` or `/usr/local`), you might need to set `PKG_CONFIG_PATH` during `npm install` and that it has been compiled with `_GLIBCXX_USE_CXX11_ABI=0`.
and `LD_LIBRARY_PATH` at runtime.
You can print the detected vips version using: `pkg-config --modversion vips-cpp` If you are using non-stadard paths (anything other than `/usr` or `/usr/local`),
you might need to set `PKG_CONFIG_PATH` during `npm install`
and `LD_LIBRARY_PATH` at runtime.
This allows the use of newer versions of libvips with older versions of sharp. This allows the use of newer versions of libvips with older versions of sharp.
For older Linux-based operating systems and 32-bit Intel CPUs, For 32-bit Intel CPUs and older Linux-based operating systems such as Centos 6,
a system-wide installation of the most suitable version of a system-wide installation of the most suitable version of
libvips and its dependencies can be achieved by running libvips and its dependencies can be achieved by running
the following command as a user with `sudo` access the following command as a user with `sudo` access
(requires `curl` and `pkg-config`): (requires `curl` and `pkg-config`):
```sh ```sh
# WARNING: This script is deprecated. You probably don't need to run it. Please read above.
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash - curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
``` ```
@@ -69,10 +71,10 @@ This can be achieved via homebrew:
brew install homebrew/science/vips brew install homebrew/science/vips
``` ```
For GIF input and WebP output suppport use: For WebP suppport use:
```sh ```sh
brew install homebrew/science/vips --with-imagemagick --with-webp brew install homebrew/science/vips --with-webp
``` ```
A missing or incorrectly configured _Xcode Command Line Tools_ installation A missing or incorrectly configured _Xcode Command Line Tools_ installation
@@ -93,7 +95,6 @@ libvips and its dependencies are fetched and stored within `node_modules\sharp`
This involves an automated HTTPS download of approximately 9MB. This involves an automated HTTPS download of approximately 9MB.
Only 64-bit (x64) `node.exe` is supported. Only 64-bit (x64) `node.exe` is supported.
The WebP format is currently unavailable on Windows.
### FreeBSD ### FreeBSD
@@ -134,12 +135,11 @@ do this on a system similar to the [Lambda Execution Environment](http://docs.aw
small t2.micro instance using the AMI ID listed in the previous link, ssh into it as ec2-user small t2.micro instance using the AMI ID listed in the previous link, ssh into it as ec2-user
and follow the instructions below. and follow the instructions below.
Install depencies: Install dependencies:
```sh ```sh
sudo yum-config-manager --enable epel curl -s https://rpm.nodesource.com/setup_4.x | sudo bash -
sudo yum install -y nodejs gcc-c++ sudo yum install -y gcc-c++ nodejs
curl -s https://www.npmjs.com/install.sh | sudo sh
``` ```
Copy your code and package.json to the instance using `scp` and create a deployment package: Copy your code and package.json to the instance using `scp` and create a deployment package:
@@ -150,7 +150,7 @@ npm install
zip -ur9 ../sharp-lambda-example.zip index.js node_modules zip -ur9 ../sharp-lambda-example.zip index.js node_modules
``` ```
You can now download your deployment ZIP using `scp` and upload it to Lambda. You can now download your deployment ZIP using `scp` and upload it to Lambda. Be sure to set your Lambda runtime to Node.js 4.3.
**Performance Tip:** To get the best performance on Lambda choose the largest memory available because this also gives you the most cpu time (a 1536 MB function is 12x faster than a 128 MB function). **Performance Tip:** To get the best performance on Lambda choose the largest memory available because this also gives you the most cpu time (a 1536 MB function is 12x faster than a 128 MB function).
@@ -158,3 +158,38 @@ You can now download your deployment ZIP using `scp` and upload it to Lambda.
* [gulp-responsive](https://www.npmjs.com/package/gulp-responsive) * [gulp-responsive](https://www.npmjs.com/package/gulp-responsive)
* [grunt-sharp](https://www.npmjs.com/package/grunt-sharp) * [grunt-sharp](https://www.npmjs.com/package/grunt-sharp)
### Security
Many users of this module process untrusted, user-supplied images,
but there are aspects of security to consider when doing so.
It is possible to compile libvips with support for various third-party image loaders.
Each of these libraries has undergone differing levels of security testing.
Whilst tools such as [American Fuzzy Lop](http://lcamtuf.coredump.cx/afl/)
and [Valgrind](http://valgrind.org/) have been used to test
the most popular web-based formats, as well as libvips itself,
you are advised to perform your own testing and sandboxing.
ImageMagick in particular has a relatively large attack surface,
which can be partially mitigated with a
[policy.xml](http://www.imagemagick.org/script/resources.php)
configuration file to prevent the use of coders known to be vulnerable.
```xml
<policymap>
<policy domain="coder" rights="none" pattern="EPHEMERAL" />
<policy domain="coder" rights="none" pattern="URL" />
<policy domain="coder" rights="none" pattern="HTTPS" />
<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="coder" rights="none" pattern="MSL" />
<policy domain="coder" rights="none" pattern="TEXT" />
<policy domain="coder" rights="none" pattern="SHOW" />
<policy domain="coder" rights="none" pattern="WIN" />
<policy domain="coder" rights="none" pattern="PLT" />
</policymap>
```
Set the `MAGICK_CONFIGURE_PATH` environment variable
to the directory containing the `policy.xml` file.

View File

@@ -2,42 +2,45 @@
### Test environment ### Test environment
* AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) (4x E5-2666 v3 @2.90GHz) * AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) (4x E5-2666 v3 @ 2.90GHz)
* Amazon Linux 2015.09.1 * Amazon Linux AMI 2016.03.1 (HVM), SSD Volume Type
* Node.js v5.5.0 * Node.js v6.2.0
### The contenders ### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.2.20 - Image processing in pure JavaScript. Bilinear interpolation only. * [jimp](https://www.npmjs.com/package/jimp) v0.2.24 - Image processing in pure JavaScript. Bilinear interpolation only.
* [lwip](https://www.npmjs.com/package/lwip) v0.0.8 - Wrapper around CImg, compiles dependencies from source. * [lwip](https://www.npmjs.com/package/lwip) v0.0.9 - Wrapper around CImg, compiles dependencies from source.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) @47c7329 - Wrapper around libmagick++, supports Buffers only. * [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) v1.9.2 - Wrapper around libmagick++, supports Buffers only.
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*". * [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
* [gm](https://www.npmjs.com/package/gm) v1.21.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility. * [gm](https://www.npmjs.com/package/gm) v1.22.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.13.0 / libvips v8.2.2 - Caching within libvips disabled to ensure a fair comparison. * sharp v0.15.0 / libvips v8.3.1 - Caching within libvips disabled to ensure a fair comparison.
### The task ### The task
Decompress a 2725x2225 JPEG image, resize to 720x480 using bicubic interpolation (where available), then compress to JPEG. Decompress a 2725x2225 JPEG image,
resize to 720x480 using Lanczos 3 resampling (where available),
then compress to JPEG.
### Results ### Results
| Module | Input | Output | Ops/sec | Speed-up | | Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: | | :----------------- | :----- | :----- | ------: | -------: |
| jimp (bilinear) | file | file | 1.04 | 1.0 | | jimp (bilinear) | file | file | 0.94 | 1.0 |
| jimp (bilinear) | buffer | buffer | 1.07 | 1.0 | | jimp (bilinear) | buffer | buffer | 0.98 | 1.0 |
| lwip | file | file | 1.13 | 1.1 | | lwip | file | file | 1.14 | 1.2 |
| lwip | buffer | buffer | 1.13 | 1.1 | | lwip | buffer | buffer | 1.14 | 1.2 |
| imagemagick-native | buffer | buffer | 1.65 | 1.6 | | imagemagick-native | buffer | buffer | 1.66 | 1.8 |
| imagemagick | file | file | 5.02 | 4.8 | | imagemagick | file | file | 5.08 | 5.4 |
| gm | buffer | buffer | 5.36 | 5.2 | | gm | buffer | buffer | 5.43 | 5.7 |
| gm | file | file | 5.39 | 5.2 | | gm | file | file | 5.46 | 5.8 |
| sharp | stream | stream | 22.00 | 21.2 | | sharp | stream | stream | 26.52 | 28.2 |
| sharp | file | file | 22.87 | 22.0 | | sharp | file | file | 28.16 | 30.0 |
| sharp | file | buffer | 23.03 | 22.1 | | sharp | file | buffer | 28.27 | 30.1 |
| sharp | buffer | file | 23.10 | 22.2 | | sharp | buffer | file | 28.42 | 30.2 |
| sharp | buffer | buffer | 23.21 | 22.3 | | sharp | buffer | buffer | 28.42 | 30.2 |
Greater performance can be expected with caching enabled (default) and using 8+ core machines. Greater libvips performance can be expected with caching enabled (default)
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
The I/O limits of the relevant (de)compression library will generally determine maximum throughput. The I/O limits of the relevant (de)compression library will generally determine maximum throughput.

144
index.js
View File

@@ -74,13 +74,14 @@ var Sharp = function(input, options) {
extendLeft: 0, extendLeft: 0,
extendRight: 0, extendRight: 0,
withoutEnlargement: false, withoutEnlargement: false,
kernel: 'lanczos3',
interpolator: 'bicubic', interpolator: 'bicubic',
// operations // operations
background: [0, 0, 0, 255], background: [0, 0, 0, 255],
flatten: false, flatten: false,
negate: false, negate: false,
blurSigma: 0, blurSigma: 0,
sharpenRadius: 0, sharpenSigma: 0,
sharpenFlat: 1, sharpenFlat: 1,
sharpenJagged: 2, sharpenJagged: 2,
threshold: 0, threshold: 0,
@@ -154,14 +155,20 @@ var isDefined = function(val) {
var isObject = function(val) { var isObject = function(val) {
return typeof val === 'object'; return typeof val === 'object';
}; };
var isBoolean = function(val) {
return typeof val === 'boolean';
};
var isBuffer = function(val) { var isBuffer = function(val) {
return typeof val === 'object' && val instanceof Buffer; return typeof val === 'object' && val instanceof Buffer;
}; };
var isString = function(val) { var isString = function(val) {
return typeof val === 'string' && val.length > 0; return typeof val === 'string' && val.length > 0;
}; };
var isNumber = function(val) {
return typeof val === 'number' && !Number.isNaN(val);
};
var isInteger = function(val) { var isInteger = function(val) {
return typeof val === 'number' && !Number.isNaN(val) && val % 1 === 0; return isNumber(val) && val % 1 === 0;
}; };
var inRange = function(val, min, max) { var inRange = function(val, min, max) {
return val >= min && val <= max; return val >= min && val <= max;
@@ -406,17 +413,17 @@ Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
Call with a sigma to use a slower, more accurate Gaussian blur. Call with a sigma to use a slower, more accurate Gaussian blur.
*/ */
Sharp.prototype.blur = function(sigma) { Sharp.prototype.blur = function(sigma) {
if (typeof sigma === 'undefined') { if (!isDefined(sigma)) {
// No arguments: default to mild blur // No arguments: default to mild blur
this.options.blurSigma = -1; this.options.blurSigma = -1;
} else if (typeof sigma === 'boolean') { } else if (isBoolean(sigma)) {
// Boolean argument: apply mild blur? // Boolean argument: apply mild blur?
this.options.blurSigma = sigma ? -1 : 0; this.options.blurSigma = sigma ? -1 : 0;
} else if (typeof sigma === 'number' && !Number.isNaN(sigma) && sigma >= 0.3 && sigma <= 1000) { } else if (isNumber(sigma) && inRange(sigma, 0.3, 1000)) {
// Numeric argument: specific sigma // Numeric argument: specific sigma
this.options.blurSigma = sigma; this.options.blurSigma = sigma;
} else { } else {
throw new Error('Invalid blur sigma (0.3 to 1000.0) ' + sigma); throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma);
} }
return this; return this;
}; };
@@ -425,38 +432,38 @@ Sharp.prototype.blur = function(sigma) {
Sharpen the output image. Sharpen the output image.
Call without a radius to use a fast, mild sharpen. Call without a radius to use a fast, mild sharpen.
Call with a radius to use a slow, accurate sharpen using the L of LAB colour space. Call with a radius to use a slow, accurate sharpen using the L of LAB colour space.
radius - size of mask in pixels, must be integer sigma - sigma of mask
flat - level of "flat" area sharpen, default 1 flat - level of "flat" area sharpen, default 1
jagged - level of "jagged" area sharpen, default 2 jagged - level of "jagged" area sharpen, default 2
*/ */
Sharp.prototype.sharpen = function(radius, flat, jagged) { Sharp.prototype.sharpen = function(sigma, flat, jagged) {
if (typeof radius === 'undefined') { if (!isDefined(sigma)) {
// No arguments: default to mild sharpen // No arguments: default to mild sharpen
this.options.sharpenRadius = -1; this.options.sharpenSigma = -1;
} else if (typeof radius === 'boolean') { } else if (isBoolean(sigma)) {
// Boolean argument: apply mild sharpen? // Boolean argument: apply mild sharpen?
this.options.sharpenRadius = radius ? -1 : 0; this.options.sharpenSigma = sigma ? -1 : 0;
} else if (typeof radius === 'number' && !Number.isNaN(radius) && (radius % 1 === 0) && radius >= 1) { } else if (isNumber(sigma) && inRange(sigma, 0.01, 10000)) {
// Numeric argument: specific radius // Numeric argument: specific sigma
this.options.sharpenRadius = radius; this.options.sharpenSigma = sigma;
// Control over flat areas // Control over flat areas
if (typeof flat !== 'undefined' && flat !== null) { if (isDefined(flat)) {
if (typeof flat === 'number' && !Number.isNaN(flat) && flat >= 0) { if (isNumber(flat) && inRange(flat, 0, 10000)) {
this.options.sharpenFlat = flat; this.options.sharpenFlat = flat;
} else { } else {
throw new Error('Invalid sharpen level for flat areas ' + flat + ' (expected >= 0)'); throw new Error('Invalid sharpen level for flat areas (0 - 10000) ' + flat);
} }
} }
// Control over jagged areas // Control over jagged areas
if (typeof jagged !== 'undefined' && jagged !== null) { if (isDefined(jagged)) {
if (typeof jagged === 'number' && !Number.isNaN(jagged) && jagged >= 0) { if (isNumber(jagged) && inRange(jagged, 0, 10000)) {
this.options.sharpenJagged = jagged; this.options.sharpenJagged = jagged;
} else { } else {
throw new Error('Invalid sharpen level for jagged areas ' + jagged + ' (expected >= 0)'); throw new Error('Invalid sharpen level for jagged areas (0 - 10000) ' + jagged);
} }
} }
} else { } else {
throw new Error('Invalid sharpen radius ' + radius + ' (expected integer >= 1)'); throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
} }
return this; return this;
}; };
@@ -474,33 +481,6 @@ Sharp.prototype.threshold = function(threshold) {
return this; return this;
}; };
/*
Set the interpolator to use for the affine transformation
*/
module.exports.interpolator = {
nearest: 'nearest',
bilinear: 'bilinear',
bicubic: 'bicubic',
nohalo: 'nohalo',
locallyBoundedBicubic: 'lbb',
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
Sharp.prototype.interpolateWith = function(interpolator) {
var isValid = false;
for (var key in module.exports.interpolator) {
if (module.exports.interpolator[key] === interpolator) {
isValid = true;
break;
}
}
if (isValid) {
this.options.interpolator = interpolator;
} else {
throw new Error('Invalid interpolator ' + interpolator);
}
return this;
};
/* /*
Darken image pre-resize (1/gamma) and brighten post-resize (gamma). Darken image pre-resize (1/gamma) and brighten post-resize (gamma).
Improves brightness of resized image in non-linear colour spaces. Improves brightness of resized image in non-linear colour spaces.
@@ -706,27 +686,75 @@ Sharp.prototype.extend = function(extend) {
return this; return this;
}; };
Sharp.prototype.resize = function(width, height) { // Kernels for reduction
if (!width) { module.exports.kernel = {
this.options.width = -1; cubic: 'cubic',
} else { lanczos2: 'lanczos2',
if (typeof width === 'number' && !Number.isNaN(width) && width % 1 === 0 && width > 0 && width <= maximum.width) { lanczos3: 'lanczos3'
};
// Interpolators for enlargement
module.exports.interpolator = {
nearest: 'nearest',
bilinear: 'bilinear',
bicubic: 'bicubic',
nohalo: 'nohalo',
lbb: 'lbb',
locallyBoundedBicubic: 'lbb',
vsqbs: 'vsqbs',
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
/*
Resize image to width x height pixels
options.kernel is the kernel to use for reductions, default 'lanczos3'
options.interpolator is the interpolator to use for enlargements, default 'bicubic'
*/
Sharp.prototype.resize = function(width, height, options) {
if (isDefined(width)) {
if (isInteger(width) && inRange(width, 1, maximum.width)) {
this.options.width = width; this.options.width = width;
} else { } else {
throw new Error('Invalid width (1 to ' + maximum.width + ') ' + width); throw new Error('Invalid width (1 to ' + maximum.width + ') ' + width);
} }
}
if (!height) {
this.options.height = -1;
} else { } else {
if (typeof height === 'number' && !Number.isNaN(height) && height % 1 === 0 && height > 0 && height <= maximum.height) { this.options.width = -1;
}
if (isDefined(height)) {
if (isInteger(height) && inRange(height, 1, maximum.height)) {
this.options.height = height; this.options.height = height;
} else { } else {
throw new Error('Invalid height (1 to ' + maximum.height + ') ' + height); throw new Error('Invalid height (1 to ' + maximum.height + ') ' + height);
} }
} else {
this.options.height = -1;
}
if (isObject(options)) {
// Kernel
if (isDefined(options.kernel)) {
if (isString(module.exports.kernel[options.kernel])) {
this.options.kernel = module.exports.kernel[options.kernel];
} else {
throw new Error('Invalid kernel ' + options.kernel);
}
}
// Interpolator
if (isDefined(options.interpolator)) {
if (isString(module.exports.interpolator[options.interpolator])) {
this.options.interpolator = module.exports.interpolator[options.interpolator];
} else {
throw new Error('Invalid interpolator ' + options.interpolator);
}
}
} }
return this; return this;
}; };
Sharp.prototype.interpolateWith = util.deprecate(function(interpolator) {
return this.resize(
this.options.width > 0 ? this.options.width : null,
this.options.height > 0 ? this.options.height : null,
{ interpolator: interpolator }
);
}, 'interpolateWith: Please use resize(w, h, { interpolator: ... }) instead');
/* /*
Limit the total number of pixels for input images Limit the total number of pixels for input images

View File

@@ -1,6 +1,6 @@
{ {
"name": "sharp", "name": "sharp",
"version": "0.14.1", "version": "0.15.0",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"contributors": [ "contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>", "Pierre Inglebert <pierre.inglebert@gmail.com>",
@@ -24,7 +24,7 @@
"Felix Bünemann <Felix.Buenemann@gmail.com>", "Felix Bünemann <Felix.Buenemann@gmail.com>",
"Samy Al Zahrani <samyalzahrany@gmail.com>" "Samy Al Zahrani <samyalzahrany@gmail.com>"
], ],
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"scripts": { "scripts": {
"clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*", "clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*",
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=30000 ./test/unit/*.js", "test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=30000 ./test/unit/*.js",
@@ -43,7 +43,10 @@
"png", "png",
"webp", "webp",
"tiff", "tiff",
"gif",
"svg",
"dzi", "dzi",
"image",
"resize", "resize",
"thumbnail", "thumbnail",
"crop", "crop",
@@ -73,7 +76,7 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"config": { "config": {
"libvips": "8.2.3" "libvips": "8.3.1"
}, },
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"

View File

@@ -19,18 +19,30 @@ export CXXFLAGS="-O3"
# Dependency version numbers # Dependency version numbers
VERSION_ZLIB=1.2.8 VERSION_ZLIB=1.2.8
VERSION_FFI=3.2.1 VERSION_FFI=3.2.1
VERSION_GLIB=2.47.6 VERSION_GLIB=2.48.0
VERSION_XML2=2.9.3 VERSION_XML2=2.9.3
VERSION_GSF=1.14.34 VERSION_GSF=1.14.36
VERSION_EXIF=0.6.21 VERSION_EXIF=0.6.21
VERSION_LCMS2=2.7 VERSION_LCMS2=2.7
VERSION_GM=1.3.23 VERSION_JPEG=1.4.90
VERSION_JPEG=1.4.2
VERSION_PNG16=1.6.21 VERSION_PNG16=1.6.21
VERSION_WEBP=0.5.0 VERSION_WEBP=0.5.0
VERSION_TIFF=4.0.6 VERSION_TIFF=4.0.6
VERSION_ORC=0.4.25 VERSION_ORC=0.4.25
VERSION_VIPS=8.2.3 VERSION_GDKPIXBUF=2.34.0
VERSION_FREETYPE=2.6.3
VERSION_FONTCONFIG=2.11.95
VERSION_HARFBUZZ=1.2.6
VERSION_PIXMAN=0.34.0
VERSION_CAIRO=1.14.6
VERSION_PANGO=1.40.1
VERSION_CROCO=0.6.11
VERSION_SVG=2.40.15
VERSION_GIF=5.1.4
VERSION_VIPS=8.3.1
# Least out-of-sync Sourceforge mirror
SOURCEFORGE_MIRROR=netix
mkdir ${DEPS}/zlib mkdir ${DEPS}/zlib
curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1 curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
@@ -44,7 +56,7 @@ cd ${DEPS}/ffi
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip
mkdir ${DEPS}/glib mkdir ${DEPS}/glib
curl -Ls https://download.gnome.org/sources/glib/2.47/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 curl -Ls https://download.gnome.org/sources/glib/2.48/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1
cd ${DEPS}/glib cd ${DEPS}/glib
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip
@@ -59,27 +71,22 @@ cd ${DEPS}/gsf
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/exif mkdir ${DEPS}/exif
curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1 curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
cd ${DEPS}/exif cd ${DEPS}/exif
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/lcms2 mkdir ${DEPS}/lcms2
curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
cd ${DEPS}/lcms2 cd ${DEPS}/lcms2
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/gm
curl -Ls http://heanet.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/${VERSION_GM}/GraphicsMagick-${VERSION_GM}.tar.xz | tar xJC ${DEPS}/gm --strip-components=1
cd ${DEPS}/gm
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip
mkdir ${DEPS}/jpeg mkdir ${DEPS}/jpeg
curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1 curl -Ls https://github.com/libjpeg-turbo/libjpeg-turbo/archive/${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
cd ${DEPS}/jpeg cd ${DEPS}/jpeg
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip autoreconf -fiv && ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip
mkdir ${DEPS}/png16 mkdir ${DEPS}/png16
curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1 curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
cd ${DEPS}/png16 cd ${DEPS}/png16
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
@@ -99,11 +106,69 @@ curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz
cd ${DEPS}/orc cd ${DEPS}/orc
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/gdkpixbuf
curl -Ls https://download.gnome.org/sources/gdk-pixbuf/2.34/gdk-pixbuf-${VERSION_GDKPIXBUF}.tar.xz | tar xJC ${DEPS}/gdkpixbuf --strip-components=1
cd ${DEPS}/gdkpixbuf
LD_LIBRARY_PATH=${TARGET}/lib ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-introspection --disable-modules --without-libpng --without-libjpeg --without-libtiff --without-gdiplus --with-included-loaders= \
&& make install-strip
mkdir ${DEPS}/freetype
curl -Ls http://download.savannah.gnu.org/releases/freetype/freetype-${VERSION_FREETYPE}.tar.gz | tar xzC ${DEPS}/freetype --strip-components=1
cd ${DEPS}/freetype
./configure --prefix=${TARGET} --enable-shared --disable-static && make install
mkdir ${DEPS}/fontconfig
curl -Ls https://www.freedesktop.org/software/fontconfig/release/fontconfig-${VERSION_FONTCONFIG}.tar.bz2 | tar xjC ${DEPS}/fontconfig --strip-components=1
cd ${DEPS}/fontconfig
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --enable-libxml2 && make install-strip
mkdir ${DEPS}/harfbuzz
curl -Ls https://www.freedesktop.org/software/harfbuzz/release/harfbuzz-${VERSION_HARFBUZZ}.tar.bz2 | tar xjC ${DEPS}/harfbuzz --strip-components=1
cd ${DEPS}/harfbuzz
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/pixman
curl -Ls http://cairographics.org/releases/pixman-${VERSION_PIXMAN}.tar.gz | tar xzC ${DEPS}/pixman --strip-components=1
cd ${DEPS}/pixman
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-libpng --disable-arm-iwmmxt && make install-strip
mkdir ${DEPS}/cairo
curl -Ls http://cairographics.org/releases/cairo-${VERSION_CAIRO}.tar.xz | tar xJC ${DEPS}/cairo --strip-components=1
cd ${DEPS}/cairo
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-xlib --disable-xcb --disable-quartz --disable-win32 --disable-egl --disable-glx --disable-wgl \
--disable-script --disable-ps --disable-gobject --disable-trace --disable-interpreter \
&& make install-strip
mkdir ${DEPS}/pango
curl -Ls https://download.gnome.org/sources/pango/1.40/pango-${VERSION_PANGO}.tar.xz | tar xJC ${DEPS}/pango --strip-components=1
cd ${DEPS}/pango
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/croco
curl -Ls https://download.gnome.org/sources/libcroco/0.6/libcroco-${VERSION_CROCO}.tar.xz | tar xJC ${DEPS}/croco --strip-components=1
cd ${DEPS}/croco
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/svg
curl -Ls https://download.gnome.org/sources/librsvg/2.40/librsvg-${VERSION_SVG}.tar.xz | tar xJC ${DEPS}/svg --strip-components=1
cd ${DEPS}/svg
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-introspection --disable-tools \
&& make install-strip
mkdir ${DEPS}/gif
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/giflib/giflib-${VERSION_GIF}.tar.gz | tar xzC ${DEPS}/gif --strip-components=1
cd ${DEPS}/gif
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/vips mkdir ${DEPS}/vips
curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1 curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.3/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
cd ${DEPS}/vips cd ${DEPS}/vips
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \ ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw --with-magickpackage=GraphicsMagick \ --disable-debug --disable-introspection --without-python --without-fftw \
--without-magick --without-pangoft2 --without-ppm --without-analyze --without-radiance \
--with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \ --with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \ --with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
&& make install-strip && make install-strip
@@ -117,20 +182,29 @@ rm -rf pkgconfig .libs *.la libvipsCC*
# Create JSON file of version numbers # Create JSON file of version numbers
cd ${TARGET} cd ${TARGET}
echo "{\n\ echo "{\n\
\"cairo\": \"${VERSION_CAIRO}\",\n\
\"croco\": \"${VERSION_CROCO}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\ \"exif\": \"${VERSION_EXIF}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\ \"ffi\": \"${VERSION_FFI}\",\n\
\"fontconfig\": \"${VERSION_FONTCONFIG}\",\n\
\"freetype\": \"${VERSION_FREETYPE}\",\n\
\"gdkpixbuf\": \"${VERSION_GDKPIXBUF}\",\n\
\"gif\": \"${VERSION_GIF}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\ \"glib\": \"${VERSION_GLIB}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\ \"gsf\": \"${VERSION_GSF}\",\n\
\"harfbuzz\": \"${VERSION_HARFBUZZ}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\ \"jpeg\": \"${VERSION_JPEG}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\ \"lcms\": \"${VERSION_LCMS2}\",\n\
\"gm\": \"${VERSION_GM}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\ \"orc\": \"${VERSION_ORC}\",\n\
\"pango\": \"${VERSION_PANGO}\",\n\
\"pixman\": \"${VERSION_PIXMAN}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\ \"png\": \"${VERSION_PNG16}\",\n\
\"svg\": \"${VERSION_SVG}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\ \"tiff\": \"${VERSION_TIFF}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\ \"vips\": \"${VERSION_VIPS}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\ \"webp\": \"${VERSION_WEBP}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\ \"xml\": \"${VERSION_XML2}\",\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\ \"zlib\": \"${VERSION_ZLIB}\"\n\
}" >lib/versions.json }" >lib/versions.json
# Create .tar.gz # Create .tar.gz

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
VERSION_VIPS=8.2.3 VERSION_VIPS=8.3.1
# Is docker available? # Is docker available?
@@ -9,7 +9,7 @@ if ! type docker >/dev/null; then
exit 1 exit 1
fi fi
# TODO: docker v1.9.0 will allow build-time args - https://github.com/docker/docker/pull/15182 # TODO: docker v1.9.0 allows build-time args - https://github.com/docker/docker/pull/15182
# Windows # Windows

View File

@@ -10,28 +10,40 @@ ENV DEPS=/deps \
RUN mkdir ${DEPS} && mkdir ${TARGET} RUN mkdir ${DEPS} && mkdir ${TARGET}
# Common build paths and flags # Common build paths and flags
ENV PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig \ ENV PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig" \
PATH=${PATH}:${TARGET}/bin \ PATH="${PATH}:${TARGET}/bin" \
CPPFLAGS=-I${TARGET}/include \ CPPFLAGS="-I${TARGET}/include" \
LDFLAGS=-L${TARGET}/lib \ LDFLAGS="-L${TARGET}/lib" \
CFLAGS="-O3" \ CFLAGS="-O3" \
CXXFLAGS="-O3" CXXFLAGS="-O3"
# Dependency version numbers # Dependency version numbers
ENV VERSION_ZLIB=1.2.8 \ ENV VERSION_ZLIB=1.2.8 \
VERSION_FFI=3.2.1 \ VERSION_FFI=3.2.1 \
VERSION_GLIB=2.47.6 \ VERSION_GLIB=2.48.0 \
VERSION_XML2=2.9.3 \ VERSION_XML2=2.9.3 \
VERSION_GSF=1.14.34 \ VERSION_GSF=1.14.36 \
VERSION_EXIF=0.6.21 \ VERSION_EXIF=0.6.21 \
VERSION_LCMS2=2.7 \ VERSION_LCMS2=2.7 \
VERSION_GM=1.3.23 \ VERSION_JPEG=1.4.90 \
VERSION_JPEG=1.4.2 \
VERSION_PNG16=1.6.21 \ VERSION_PNG16=1.6.21 \
VERSION_WEBP=0.5.0 \ VERSION_WEBP=0.5.0 \
VERSION_TIFF=4.0.6 \ VERSION_TIFF=4.0.6 \
VERSION_ORC=0.4.25 \ VERSION_ORC=0.4.25 \
VERSION_VIPS=8.2.3 VERSION_GDKPIXBUF=2.34.0 \
VERSION_FREETYPE=2.6.3 \
VERSION_FONTCONFIG=2.11.95 \
VERSION_HARFBUZZ=1.2.6 \
VERSION_PIXMAN=0.34.0 \
VERSION_CAIRO=1.14.6 \
VERSION_PANGO=1.40.1 \
VERSION_CROCO=0.6.11 \
VERSION_SVG=2.40.15 \
VERSION_GIF=5.1.4 \
VERSION_VIPS=8.3.1
# Least out-of-sync Sourceforge mirror
ENV SOURCEFORGE_MIRROR=netix
RUN mkdir ${DEPS}/zlib RUN mkdir ${DEPS}/zlib
RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1 RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
@@ -45,14 +57,18 @@ WORKDIR ${DEPS}/ffi
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip
RUN mkdir ${DEPS}/glib RUN mkdir ${DEPS}/glib
RUN curl -Ls https://download.gnome.org/sources/glib/2.47/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 RUN curl -Ls https://download.gnome.org/sources/glib/2.48/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1
WORKDIR ${DEPS}/glib WORKDIR ${DEPS}/glib
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip RUN CFLAGS="${CFLAGS} -Wl,--default-symver" CXXFLAGS="${CXXFLAGS} -Wl,--default-symver" \
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip
RUN mkdir ${DEPS}/xml2 RUN mkdir ${DEPS}/xml2
RUN curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1 RUN curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1
WORKDIR ${DEPS}/xml2 WORKDIR ${DEPS}/xml2
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--without-python --without-debug --without-docbook --without-ftp --without-html --without-legacy \
--without-pattern --without-push --without-regexps --without-schemas --without-schematron --with-zlib=${TARGET} \
&& make install-strip
RUN mkdir ${DEPS}/gsf RUN mkdir ${DEPS}/gsf
RUN curl -Ls https://download.gnome.org/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1 RUN curl -Ls https://download.gnome.org/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1
@@ -60,27 +76,22 @@ WORKDIR ${DEPS}/gsf
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/exif RUN mkdir ${DEPS}/exif
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1 RUN curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
WORKDIR ${DEPS}/exif WORKDIR ${DEPS}/exif
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/lcms2 RUN mkdir ${DEPS}/lcms2
RUN curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 RUN curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
WORKDIR ${DEPS}/lcms2 WORKDIR ${DEPS}/lcms2
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/gm
RUN curl -Ls http://heanet.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/${VERSION_GM}/GraphicsMagick-${VERSION_GM}.tar.xz | tar xJC ${DEPS}/gm --strip-components=1
WORKDIR ${DEPS}/gm
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip
RUN mkdir ${DEPS}/jpeg RUN mkdir ${DEPS}/jpeg
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1 RUN curl -Ls https://github.com/libjpeg-turbo/libjpeg-turbo/archive/${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
WORKDIR ${DEPS}/jpeg WORKDIR ${DEPS}/jpeg
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip RUN autoreconf -fiv && ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip
RUN mkdir ${DEPS}/png16 RUN mkdir ${DEPS}/png16
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1 RUN curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
WORKDIR ${DEPS}/png16 WORKDIR ${DEPS}/png16
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
@@ -99,12 +110,74 @@ RUN mkdir ${DEPS}/orc
RUN curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1 RUN curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1
WORKDIR ${DEPS}/orc WORKDIR ${DEPS}/orc
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN rm ${TARGET}/lib/liborc-test-*
RUN mkdir ${DEPS}/gdkpixbuf
RUN curl -Ls https://download.gnome.org/sources/gdk-pixbuf/2.34/gdk-pixbuf-${VERSION_GDKPIXBUF}.tar.xz | tar xJC ${DEPS}/gdkpixbuf --strip-components=1
WORKDIR ${DEPS}/gdkpixbuf
RUN LD_LIBRARY_PATH=${TARGET}/lib ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-introspection --disable-modules --without-libpng --without-libjpeg --without-libtiff --without-gdiplus --with-included-loaders= \
&& make install-strip
RUN mkdir ${DEPS}/freetype
RUN curl -Ls http://download.savannah.gnu.org/releases/freetype/freetype-${VERSION_FREETYPE}.tar.gz | tar xzC ${DEPS}/freetype --strip-components=1
WORKDIR ${DEPS}/freetype
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static && make install
RUN mkdir ${DEPS}/fontconfig
RUN curl -Ls https://www.freedesktop.org/software/fontconfig/release/fontconfig-${VERSION_FONTCONFIG}.tar.bz2 | tar xjC ${DEPS}/fontconfig --strip-components=1
WORKDIR ${DEPS}/fontconfig
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --enable-libxml2 && make install-strip
RUN mkdir ${DEPS}/harfbuzz
RUN curl -Ls https://www.freedesktop.org/software/harfbuzz/release/harfbuzz-${VERSION_HARFBUZZ}.tar.bz2 | tar xjC ${DEPS}/harfbuzz --strip-components=1
WORKDIR ${DEPS}/harfbuzz
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/pixman
RUN curl -Ls http://cairographics.org/releases/pixman-${VERSION_PIXMAN}.tar.gz | tar xzC ${DEPS}/pixman --strip-components=1
WORKDIR ${DEPS}/pixman
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-libpng && make install-strip
RUN mkdir ${DEPS}/cairo
RUN curl -Ls http://cairographics.org/releases/cairo-${VERSION_CAIRO}.tar.xz | tar xJC ${DEPS}/cairo --strip-components=1
WORKDIR ${DEPS}/cairo
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-xlib --disable-xcb --disable-quartz --disable-win32 --disable-egl --disable-glx --disable-wgl \
--disable-script --disable-ps --disable-gobject --disable-trace --disable-interpreter \
&& make install-strip
RUN mkdir ${DEPS}/pango
RUN curl -Ls https://download.gnome.org/sources/pango/1.40/pango-${VERSION_PANGO}.tar.xz | tar xJC ${DEPS}/pango --strip-components=1
WORKDIR ${DEPS}/pango
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/croco
RUN curl -Ls https://download.gnome.org/sources/libcroco/0.6/libcroco-${VERSION_CROCO}.tar.xz | tar xJC ${DEPS}/croco --strip-components=1
WORKDIR ${DEPS}/croco
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/svg
RUN curl -Ls https://download.gnome.org/sources/librsvg/2.40/librsvg-${VERSION_SVG}.tar.xz | tar xJC ${DEPS}/svg --strip-components=1
WORKDIR ${DEPS}/svg
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-introspection --disable-tools \
&& make install-strip
RUN mkdir ${DEPS}/gif
RUN curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/giflib/giflib-${VERSION_GIF}.tar.gz | tar xzC ${DEPS}/gif --strip-components=1
WORKDIR ${DEPS}/gif
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/vips RUN mkdir ${DEPS}/vips
RUN curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1 RUN curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.3/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
#RUN apt-get install -y swig gobject-introspection gettext glib2.0-dev
#RUN curl -Ls https://github.com/jcupitt/libvips/archive/master.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
WORKDIR ${DEPS}/vips WORKDIR ${DEPS}/vips
#RUN ./bootstrap.sh
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \ RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw --with-magickpackage=GraphicsMagick \ --disable-debug --disable-introspection --without-python --without-fftw \
--without-magick --without-pangoft2 --without-ppm --without-analyze --without-radiance \
--with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \ --with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \ --with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
&& make install-strip && make install-strip
@@ -118,20 +191,29 @@ RUN rm -rf pkgconfig .libs *.la libvipsCC*
# Create JSON file of version numbers # Create JSON file of version numbers
WORKDIR ${TARGET} WORKDIR ${TARGET}
RUN echo "{\n\ RUN echo "{\n\
\"cairo\": \"${VERSION_CAIRO}\",\n\
\"croco\": \"${VERSION_CROCO}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\ \"exif\": \"${VERSION_EXIF}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\ \"ffi\": \"${VERSION_FFI}\",\n\
\"fontconfig\": \"${VERSION_FONTCONFIG}\",\n\
\"freetype\": \"${VERSION_FREETYPE}\",\n\
\"gdkpixbuf\": \"${VERSION_GDKPIXBUF}\",\n\
\"gif\": \"${VERSION_GIF}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\ \"glib\": \"${VERSION_GLIB}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\ \"gsf\": \"${VERSION_GSF}\",\n\
\"harfbuzz\": \"${VERSION_HARFBUZZ}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\ \"jpeg\": \"${VERSION_JPEG}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\ \"lcms\": \"${VERSION_LCMS2}\",\n\
\"gm\": \"${VERSION_GM}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\ \"orc\": \"${VERSION_ORC}\",\n\
\"pango\": \"${VERSION_PANGO}\",\n\
\"pixman\": \"${VERSION_PIXMAN}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\ \"png\": \"${VERSION_PNG16}\",\n\
\"svg\": \"${VERSION_SVG}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\ \"tiff\": \"${VERSION_TIFF}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\ \"vips\": \"${VERSION_VIPS}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\ \"webp\": \"${VERSION_WEBP}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\ \"xml\": \"${VERSION_XML2}\",\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\ \"zlib\": \"${VERSION_ZLIB}\"\n\
}" >lib/versions.json }" >lib/versions.json
# Create .tar.gz # Create .tar.gz

View File

@@ -6,34 +6,33 @@ if ! type docker >/dev/null; then
exit 1 exit 1
fi fi
version_node=4.4.2
test="npm run clean; npm install --unsafe-perm; npm test" test="npm run clean; npm install --unsafe-perm; npm test"
# Debian 7, 8 # Debian 7, 8
# Ubuntu 12.04, 14.04 # Ubuntu 14.04
for dist in wheezy jessie precise trusty; do for dist in wheezy jessie trusty; do
echo "Testing $dist..." echo "Testing $dist..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/$dist:0.12 >packaging/$dist.log 2>&1 sh -c "cd /v; ./packaging/test/debian.sh; $test"; if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/$dist:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; ./packaging/test/debian.sh; $test";
then echo "$dist OK" then echo "$dist OK"
else echo "$dist fail" && cat packaging/$dist.log else echo "$dist fail" && cat packaging/$dist.log
fi fi
done done
# Centos 6
echo "Testing centos6..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/centos6:0.12 >packaging/centos6.log 2>&1 sh -c "cd /v; source ./packaging/test/centos6.sh; ./preinstall.sh; $test";
then echo "centos6 OK"
else echo "centos6 fail" && cat packaging/centos6.log
fi
# Centos 7 # Centos 7
# Fedora 20, 21 echo "Testing centos7..."
for dist in centos7 fedora20 fedora21; do if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/centos7:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; $test";
echo "Testing $dist..." then echo "$dist OK"
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/$dist:0.12 >packaging/$dist.log 2>&1 sh -c "cd /v; $test"; else echo "$dist fail" && cat packaging/$dist.log
then echo "$dist OK" fi
else echo "$dist fail" && cat packaging/$dist.log
fi # Fedora 22
done echo "Testing fedora22..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/fedora22:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; $test";
then echo "$dist OK"
else echo "$dist fail" && cat packaging/$dist.log
fi
# openSUSE 13.2 # openSUSE 13.2
echo "Testing opensuse..." echo "Testing opensuse..."

View File

@@ -1,8 +0,0 @@
#!/bin/sh
# Install C++11 compatible version of g++ on Centos 6
curl -o /etc/yum.repos.d/devtools-1.1.repo http://people.centos.org/tru/devtools-1.1/devtools-1.1.repo
yum install -y devtoolset-1.1
export CC=/opt/centos/devtoolset-1.1/root/usr/bin/gcc
export CPP=/opt/centos/devtoolset-1.1/root/usr/bin/cpp
export CXX=/opt/centos/devtoolset-1.1/root/usr/bin/c++

View File

@@ -1,19 +1,19 @@
FROM ubuntu:precise FROM debian:wheezy
MAINTAINER Lovell Fuller <npm@lovell.info> MAINTAINER Lovell Fuller <npm@lovell.info>
RUN apt-get update && apt-get install -y curl zip RUN apt-get update && apt-get install -y curl zip
ENV VERSION_VIPS=8.2.3 ENV VERSION_VIPS=8.3.1
# Fetch and unzip # Fetch and unzip
RUN mkdir /vips RUN mkdir /vips
WORKDIR /vips WORKDIR /vips
RUN curl -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.zip RUN curl -L -O https://github.com/lovell/build-win64/releases/download/v${VERSION_VIPS}/vips-dev-w64-web-${VERSION_VIPS}.zip
RUN unzip vips-dev-w64-8.2.zip RUN unzip vips-dev-w64-web-${VERSION_VIPS}.zip
# Clean and zip # Clean and zip
WORKDIR /vips/vips-dev-8.2 WORKDIR /vips/vips-dev-8.3
RUN rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll bin/libstdc++-6.dll RUN rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll
RUN cp bin/*.dll lib/ RUN cp bin/*.dll lib/
RUN cp -r lib64/* lib/ RUN cp -r lib64/* lib/

View File

@@ -1,28 +1,19 @@
#!/bin/sh #!/bin/sh
# This script is no longer required on most # Use of this script is deprecated
# 64-bit Linux systems when using sharp v0.12.0+
# See http://sharp.dimens.io/page/install#linux echo
echo "WARNING: This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
echo
echo "See http://sharp.dimens.io/page/install#linux"
echo
echo "If you really, really need this script, it will attempt"
echo "to globally install libvips if not already available."
echo
# If you really need this script, it will attempt to vips_version_minimum=8.3.1
# globally install libvips if not already available. vips_version_latest_major_minor=8.3
vips_version_latest_patch=1
# Supports:
# * Debian Linux
# * Debian 7, 8
# * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
# * Mint 13, 17
# * Elementary 0.3
# * Red Hat Linux
# * RHEL/Centos/Scientific 6, 7
# * Fedora 21, 22, 23
# * Amazon Linux 2015.03, 2015.09
# * OpenSuse 13
vips_version_minimum=8.2.3
vips_version_latest_major_minor=8.2
vips_version_latest_patch=3
openslide_version_minimum=3.4.0 openslide_version_minimum=3.4.0
openslide_version_latest_major_minor=3.4 openslide_version_latest_major_minor=3.4
@@ -33,7 +24,7 @@ install_libvips_from_source() {
curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch
./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" ./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1
make make
make install make install
cd .. cd ..
@@ -139,7 +130,7 @@ if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_ex
DISTRO=$(lsb_release -c -s) DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'" echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in case "$DISTRO" in
jessie|vivid|wily) jessie|vivid|wily|xenial)
# Debian 8, Ubuntu 15 # Debian 8, Ubuntu 15
echo "Installing libopenslide via apt-get" echo "Installing libopenslide via apt-get"
apt-get install -y libopenslide-dev apt-get install -y libopenslide-dev

View File

@@ -66,6 +66,9 @@ namespace sharp {
case ImageType::PNG: id = "png"; break; case ImageType::PNG: id = "png"; break;
case ImageType::WEBP: id = "webp"; break; case ImageType::WEBP: id = "webp"; break;
case ImageType::TIFF: id = "tiff"; break; case ImageType::TIFF: id = "tiff"; break;
case ImageType::GIF: id = "gif"; break;
case ImageType::SVG: id = "svg"; break;
case ImageType::PDF: id = "pdf"; break;
case ImageType::MAGICK: id = "magick"; break; case ImageType::MAGICK: id = "magick"; break;
case ImageType::OPENSLIDE: id = "openslide"; break; case ImageType::OPENSLIDE: id = "openslide"; break;
case ImageType::PPM: id = "ppm"; break; case ImageType::PPM: id = "ppm"; break;
@@ -92,6 +95,12 @@ namespace sharp {
imageType = ImageType::WEBP; imageType = ImageType::WEBP;
} else if (EndsWith(loader, "TiffBuffer")) { } else if (EndsWith(loader, "TiffBuffer")) {
imageType = ImageType::TIFF; imageType = ImageType::TIFF;
} else if (EndsWith(loader, "GifBuffer")) {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgBuffer")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "PdfBuffer")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "MagickBuffer")) { } else if (EndsWith(loader, "MagickBuffer")) {
imageType = ImageType::MAGICK; imageType = ImageType::MAGICK;
} }
@@ -117,6 +126,12 @@ namespace sharp {
imageType = ImageType::OPENSLIDE; imageType = ImageType::OPENSLIDE;
} else if (EndsWith(loader, "TiffFile")) { } else if (EndsWith(loader, "TiffFile")) {
imageType = ImageType::TIFF; imageType = ImageType::TIFF;
} else if (EndsWith(loader, "GifFile")) {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgFile")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "PdfFile")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "Ppm")) { } else if (EndsWith(loader, "Ppm")) {
imageType = ImageType::PPM; imageType = ImageType::PPM;
} else if (EndsWith(loader, "Fits")) { } else if (EndsWith(loader, "Fits")) {

View File

@@ -11,16 +11,19 @@ using vips::VImage;
namespace sharp { namespace sharp {
enum class ImageType { enum class ImageType {
UNKNOWN,
JPEG, JPEG,
PNG, PNG,
WEBP, WEBP,
TIFF, TIFF,
GIF,
SVG,
PDF,
MAGICK, MAGICK,
OPENSLIDE, OPENSLIDE,
PPM, PPM,
FITS, FITS,
RAW RAW,
UNKNOWN
}; };
// How many tasks are in the queue? // How many tasks are in the queue?

View File

@@ -711,4 +711,499 @@ VImage::maxpos( VOption *options )
return( std::complex<double>( x, y ) ); return( std::complex<double>( x, y ) );
} }
// Operator overloads
VImage
VImage::operator[]( int index )
{
return( this->extract_band( index ) );
}
std::vector<double>
VImage::operator()( int x, int y )
{
return( this->getpoint( x, y ) );
}
VImage
operator+( VImage a, VImage b )
{
return( a.add( b ) );
}
VImage
operator+( double a, VImage b )
{
return( b.linear( 1.0, a ) );
}
VImage
operator+( VImage a, double b )
{
return( a.linear( 1.0, b ) );
}
VImage
operator+( std::vector<double> a, VImage b )
{
return( b.linear( 1.0, a ) );
}
VImage
operator+( VImage a, std::vector<double> b )
{
return( a.linear( 1.0, b ) );
}
VImage
operator-( VImage a, VImage b )
{
return( a.subtract( b ) );
}
VImage operator-( double a, VImage b )
{
return( b.linear( -1.0, a ) );
}
VImage
operator-( VImage a, double b )
{
return( a.linear( 1.0, -b ) );
}
VImage
operator-( std::vector<double> a, VImage b )
{
return( b.linear( -1.0, a ) );
}
VImage
operator-( VImage a, std::vector<double> b )
{
return( a.linear( 1.0, vips::negate( b ) ) );
}
VImage
operator-( VImage a )
{
return( a * -1 );
}
VImage
operator*( VImage a, VImage b )
{
return( a.multiply( b ) );
}
VImage
operator*( double a, VImage b )
{
return( b.linear( a, 0.0 ) );
}
VImage
operator*( VImage a, double b )
{
return( a.linear( b, 0.0 ) );
}
VImage
operator*( std::vector<double> a, VImage b )
{
return( b.linear( a, 0.0 ) );
}
VImage
operator*( VImage a, std::vector<double> b )
{
return( a.linear( b, 0.0 ) );
}
VImage
operator/( VImage a, VImage b )
{
return( a.divide( b ) );
}
VImage
operator/( double a, VImage b )
{
return( b.pow( -1.0 ).linear( a, 0.0 ) );
}
VImage
operator/( VImage a, double b )
{
return( a.linear( 1.0 / b, 0.0 ) );
}
VImage
operator/( std::vector<double> a, VImage b )
{
return( b.pow( -1.0 ).linear( a, 0.0 ) );
}
VImage
operator/( VImage a, std::vector<double> b )
{
return( a.linear( vips::invert( b ), 0.0 ) );
}
VImage
operator%( VImage a, VImage b )
{
return( a.remainder( b ) );
}
VImage
operator%( VImage a, double b )
{
return( a.remainder_const( to_vector( b ) ) );
}
VImage
operator%( VImage a, std::vector<double> b )
{
return( a.remainder_const( b ) );
}
VImage
operator<( VImage a, VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_LESS ) );
}
VImage
operator<( double a, VImage b )
{
return( b.relational_const( to_vector( a ),
VIPS_OPERATION_RELATIONAL_MORE ) );
}
VImage
operator<( VImage a, double b )
{
return( a.relational_const( to_vector( b ),
VIPS_OPERATION_RELATIONAL_LESS ) );
}
VImage
operator<( std::vector<double> a, VImage b )
{
return( b.relational_const( a,
VIPS_OPERATION_RELATIONAL_MORE ) );
}
VImage
operator<( VImage a, std::vector<double> b )
{
return( a.relational_const( b,
VIPS_OPERATION_RELATIONAL_LESS ) );
}
VImage
operator<=( VImage a, VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_LESSEQ ) );
}
VImage
operator<=( double a, VImage b )
{
return( b.relational_const( to_vector( a ),
VIPS_OPERATION_RELATIONAL_MOREEQ ) );
}
VImage
operator<=( VImage a, double b )
{
return( a.relational_const( to_vector( b ),
VIPS_OPERATION_RELATIONAL_LESSEQ ) );
}
VImage
operator<=( std::vector<double> a, VImage b )
{
return( b.relational_const( a,
VIPS_OPERATION_RELATIONAL_MOREEQ ) );
}
VImage
operator<=( VImage a, std::vector<double> b )
{
return( a.relational_const( b,
VIPS_OPERATION_RELATIONAL_LESSEQ ) );
}
VImage
operator>( VImage a, VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_MORE ) );
}
VImage
operator>( double a, VImage b )
{
return( b.relational_const( to_vector( a ),
VIPS_OPERATION_RELATIONAL_LESS ) );
}
VImage
operator>( VImage a, double b )
{
return( a.relational_const( to_vector( b ),
VIPS_OPERATION_RELATIONAL_MORE ) );
}
VImage
operator>( std::vector<double> a, VImage b )
{
return( b.relational_const( a,
VIPS_OPERATION_RELATIONAL_LESS ) );
}
VImage
operator>( VImage a, std::vector<double> b )
{
return( a.relational_const( b,
VIPS_OPERATION_RELATIONAL_MORE ) );
}
VImage
operator>=( VImage a, VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_MOREEQ ) );
}
VImage
operator>=( double a, VImage b )
{
return( b.relational_const( to_vector( a ),
VIPS_OPERATION_RELATIONAL_LESSEQ ) );
}
VImage
operator>=( VImage a, double b )
{
return( a.relational_const( to_vector( b ),
VIPS_OPERATION_RELATIONAL_MOREEQ ) );
}
VImage
operator>=( std::vector<double> a, VImage b )
{
return( b.relational_const( a,
VIPS_OPERATION_RELATIONAL_LESSEQ ) );
}
VImage
operator>=( VImage a, std::vector<double> b )
{
return( a.relational_const( b,
VIPS_OPERATION_RELATIONAL_MOREEQ ) );
}
VImage
operator==( VImage a, VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_EQUAL ) );
}
VImage
operator==( double a, VImage b )
{
return( b.relational_const( to_vector( a ),
VIPS_OPERATION_RELATIONAL_EQUAL ) );
}
VImage
operator==( VImage a, double b )
{
return( a.relational_const( to_vector( b ),
VIPS_OPERATION_RELATIONAL_EQUAL ) );
}
VImage
operator==( std::vector<double> a, VImage b )
{
return( b.relational_const( a,
VIPS_OPERATION_RELATIONAL_EQUAL ) );
}
VImage
operator==( VImage a, std::vector<double> b )
{
return( a.relational_const( b,
VIPS_OPERATION_RELATIONAL_EQUAL ) );
}
VImage
operator!=( VImage a, VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_NOTEQ ) );
}
VImage
operator!=( double a, VImage b )
{
return( b.relational_const( to_vector( a ),
VIPS_OPERATION_RELATIONAL_NOTEQ ) );
}
VImage
operator!=( VImage a, double b )
{
return( a.relational_const( to_vector( b ),
VIPS_OPERATION_RELATIONAL_NOTEQ ) );
}
VImage
operator!=( std::vector<double> a, VImage b )
{
return( b.relational_const( a,
VIPS_OPERATION_RELATIONAL_NOTEQ ) );
}
VImage
operator!=( VImage a, std::vector<double> b )
{
return( a.relational_const( b,
VIPS_OPERATION_RELATIONAL_NOTEQ ) );
}
VImage
operator&( VImage a, VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_AND ) );
}
VImage
operator&( double a, VImage b )
{
return( b.boolean_const( to_vector( a ),
VIPS_OPERATION_BOOLEAN_AND ) );
}
VImage
operator&( VImage a, double b )
{
return( a.boolean_const( to_vector( b ),
VIPS_OPERATION_BOOLEAN_AND ) );
}
VImage
operator&( std::vector<double> a, VImage b )
{
return( b.boolean_const( a, VIPS_OPERATION_BOOLEAN_AND ) );
}
VImage
operator&( VImage a, std::vector<double> b )
{
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_AND ) );
}
VImage
operator|( VImage a, VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_OR ) );
}
VImage
operator|( double a, VImage b )
{
return( b.boolean_const( to_vector( a ),
VIPS_OPERATION_BOOLEAN_OR ) );
}
VImage
operator|( VImage a, double b )
{
return( a.boolean_const( to_vector( b ),
VIPS_OPERATION_BOOLEAN_OR ) );
}
VImage
operator|( std::vector<double> a, VImage b )
{
return( b.boolean_const( a, VIPS_OPERATION_BOOLEAN_OR ) );
}
VImage
operator|( VImage a, std::vector<double> b )
{
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_OR ) );
}
VImage
operator^( VImage a, VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_EOR ) );
}
VImage
operator^( double a, VImage b )
{
return( b.boolean_const( to_vector( a ),
VIPS_OPERATION_BOOLEAN_EOR ) );
}
VImage
operator^( VImage a, double b )
{
return( a.boolean_const( to_vector( b ),
VIPS_OPERATION_BOOLEAN_EOR ) );
}
VImage
operator^( std::vector<double> a, VImage b )
{
return( b.boolean_const( a, VIPS_OPERATION_BOOLEAN_EOR ) );
}
VImage
operator^( VImage a, std::vector<double> b )
{
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_EOR ) );
}
VImage
operator<<( VImage a, VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_LSHIFT ) );
}
VImage
operator<<( VImage a, double b )
{
return( a.boolean_const( to_vector( b ),
VIPS_OPERATION_BOOLEAN_LSHIFT ) );
}
VImage
operator<<( VImage a, std::vector<double> b )
{
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_LSHIFT ) );
}
VImage
operator>>( VImage a, VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_RSHIFT ) );
}
VImage
operator>>( VImage a, double b )
{
return( a.boolean_const( to_vector( b ),
VIPS_OPERATION_BOOLEAN_RSHIFT ) );
}
VImage
operator>>( VImage a, std::vector<double> b )
{
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_RSHIFT ) );
}
VIPS_NAMESPACE_END VIPS_NAMESPACE_END

View File

@@ -1,5 +1,5 @@
// bodies for vips operations // bodies for vips operations
// Sat Jan 9 15:05:58 GMT 2016 // Fri Feb 12 20:03:53 GMT 2016
// this file is generated automatically, do not edit! // this file is generated automatically, do not edit!
void VImage::system( char * cmd_format , VOption *options ) void VImage::system( char * cmd_format , VOption *options )
@@ -1408,6 +1408,78 @@ VImage VImage::vipsload( char * filename , VOption *options )
return( out ); return( out );
} }
VImage VImage::pdfload( char * filename , VOption *options )
{
VImage out;
call( "pdfload" ,
(options ? options : VImage::option()) ->
set( "filename", filename ) ->
set( "out", &out ) );
return( out );
}
VImage VImage::pdfload_buffer( VipsBlob * buffer , VOption *options )
{
VImage out;
call( "pdfload_buffer" ,
(options ? options : VImage::option()) ->
set( "buffer", buffer ) ->
set( "out", &out ) );
return( out );
}
VImage VImage::svgload( char * filename , VOption *options )
{
VImage out;
call( "svgload" ,
(options ? options : VImage::option()) ->
set( "filename", filename ) ->
set( "out", &out ) );
return( out );
}
VImage VImage::svgload_buffer( VipsBlob * buffer , VOption *options )
{
VImage out;
call( "svgload_buffer" ,
(options ? options : VImage::option()) ->
set( "buffer", buffer ) ->
set( "out", &out ) );
return( out );
}
VImage VImage::gifload( char * filename , VOption *options )
{
VImage out;
call( "gifload" ,
(options ? options : VImage::option()) ->
set( "filename", filename ) ->
set( "out", &out ) );
return( out );
}
VImage VImage::gifload_buffer( VipsBlob * buffer , VOption *options )
{
VImage out;
call( "gifload_buffer" ,
(options ? options : VImage::option()) ->
set( "buffer", buffer ) ->
set( "out", &out ) );
return( out );
}
VImage VImage::pngload( char * filename , VOption *options ) VImage VImage::pngload( char * filename , VOption *options )
{ {
VImage out; VImage out;
@@ -1783,11 +1855,37 @@ VImage VImage::shrinkv( int yshrink , VOption *options )
return( out ); return( out );
} }
VImage VImage::shrink2( double xshrink , double yshrink , VOption *options ) VImage VImage::reduceh( double xshrink , VOption *options )
{ {
VImage out; VImage out;
call( "shrink2" , call( "reduceh" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "xshrink", xshrink ) );
return( out );
}
VImage VImage::reducev( double yshrink , VOption *options )
{
VImage out;
call( "reducev" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "yshrink", yshrink ) );
return( out );
}
VImage VImage::reduce( double xshrink , double yshrink , VOption *options )
{
VImage out;
call( "reduce" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->

View File

@@ -135,10 +135,10 @@ namespace sharp {
} }
/* /*
* Gaussian blur (use sigma <0 for fast blur) * Gaussian blur. Use sigma of -1.0 for fast blur.
*/ */
VImage Blur(VImage image, double const sigma) { VImage Blur(VImage image, double const sigma) {
if (sigma < 0.0) { if (sigma == -1.0) {
// Fast, mild blur - averages neighbouring pixels // Fast, mild blur - averages neighbouring pixels
VImage blur = VImage::new_matrixv(3, 3, VImage blur = VImage::new_matrixv(3, 3,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
@@ -153,10 +153,10 @@ namespace sharp {
} }
/* /*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen. * Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/ */
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged) { VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged) {
if (radius == -1) { if (sigma == -1.0) {
// Fast, mild sharpen // Fast, mild sharpen
VImage sharpen = VImage::new_matrixv(3, 3, VImage sharpen = VImage::new_matrixv(3, 3,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
@@ -167,7 +167,7 @@ namespace sharp {
} else { } else {
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas // Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
return image.sharpen( return image.sharpen(
VImage::option()->set("radius", radius)->set("m1", flat)->set("m2", jagged) VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged)
); );
} }
} }
@@ -250,4 +250,22 @@ namespace sharp {
return image.hist_find().hist_entropy(); return image.hist_find().hist_entropy();
} }
/*
Insert a tile cache to prevent over-computation of any previous operations in the pipeline
*/
VImage TileCache(VImage image, double const factor) {
int tile_width;
int tile_height;
int scanline_count;
vips_get_tile_size(image.get_image(), &tile_width, &tile_height, &scanline_count);
double const need_lines = 1.2 * scanline_count / factor;
return image.tilecache(VImage::option()
->set("tile_width", image.width())
->set("tile_height", 10)
->set("max_tiles", static_cast<int>(round(1.0 + need_lines / 10.0)))
->set("access", VIPS_ACCESS_SEQUENTIAL)
->set("threaded", TRUE)
);
}
} // namespace sharp } // namespace sharp

View File

@@ -25,14 +25,14 @@ namespace sharp {
VImage Gamma(VImage image, double const exponent); VImage Gamma(VImage image, double const exponent);
/* /*
* Gaussian blur. Use sigma of -1 for fast blur. * Gaussian blur. Use sigma of -1.0 for fast blur.
*/ */
VImage Blur(VImage image, double const sigma); VImage Blur(VImage image, double const sigma);
/* /*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen. * Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/ */
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged); VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
/* /*
Calculate crop area based on image entropy Calculate crop area based on image entropy
@@ -44,6 +44,11 @@ namespace sharp {
*/ */
double Entropy(VImage image); double Entropy(VImage image);
/*
Insert a tile cache to prevent over-computation of any previous operations in the pipeline
*/
VImage TileCache(VImage image, double const factor);
} // namespace sharp } // namespace sharp
#endif // SRC_OPERATIONS_H_ #endif // SRC_OPERATIONS_H_

View File

@@ -50,6 +50,7 @@ using sharp::Gamma;
using sharp::Blur; using sharp::Blur;
using sharp::Sharpen; using sharp::Sharpen;
using sharp::EntropyCrop; using sharp::EntropyCrop;
using sharp::TileCache;
using sharp::ImageType; using sharp::ImageType;
using sharp::ImageTypeId; using sharp::ImageTypeId;
@@ -123,11 +124,16 @@ class PipelineWorker : public AsyncWorker {
if (inputImageType != ImageType::UNKNOWN) { if (inputImageType != ImageType::UNKNOWN) {
try { try {
VOption *option = VImage::option()->set("access", baton->accessMethod); VOption *option = VImage::option()->set("access", baton->accessMethod);
if (inputImageType == ImageType::SVG || inputImageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(baton->density));
}
if (inputImageType == ImageType::MAGICK) { if (inputImageType == ImageType::MAGICK) {
option->set("density", std::to_string(baton->density).data()); option->set("density", std::to_string(baton->density).data());
} }
image = VImage::new_from_buffer(baton->bufferIn, baton->bufferInLength, nullptr, option); image = VImage::new_from_buffer(baton->bufferIn, baton->bufferInLength, nullptr, option);
if (inputImageType == ImageType::MAGICK) { if (inputImageType == ImageType::SVG ||
inputImageType == ImageType::PDF ||
inputImageType == ImageType::MAGICK) {
SetDensity(image, baton->density); SetDensity(image, baton->density);
} }
} catch (...) { } catch (...) {
@@ -144,11 +150,16 @@ class PipelineWorker : public AsyncWorker {
if (inputImageType != ImageType::UNKNOWN) { if (inputImageType != ImageType::UNKNOWN) {
try { try {
VOption *option = VImage::option()->set("access", baton->accessMethod); VOption *option = VImage::option()->set("access", baton->accessMethod);
if (inputImageType == ImageType::SVG || inputImageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(baton->density));
}
if (inputImageType == ImageType::MAGICK) { if (inputImageType == ImageType::MAGICK) {
option->set("density", std::to_string(baton->density).data()); option->set("density", std::to_string(baton->density).data());
} }
image = VImage::new_from_file(baton->fileIn.data(), option); image = VImage::new_from_file(baton->fileIn.data(), option);
if (inputImageType == ImageType::MAGICK) { if (inputImageType == ImageType::SVG ||
inputImageType == ImageType::PDF ||
inputImageType == ImageType::MAGICK) {
SetDensity(image, baton->density); SetDensity(image, baton->density);
} }
} catch (...) { } catch (...) {
@@ -205,17 +216,13 @@ class PipelineWorker : public AsyncWorker {
std::swap(inputWidth, inputHeight); std::swap(inputWidth, inputHeight);
} }
// Get window size of interpolator, used for determining shrink vs affine
VInterpolate interpolator = VInterpolate::new_from_name(baton->interpolator.data());
int interpolatorWindowSize = vips_interpolate_get_window_size(interpolator.get_interpolate());
// Scaling calculations // Scaling calculations
double xfactor = 1.0; double xfactor = 1.0;
double yfactor = 1.0; double yfactor = 1.0;
if (baton->width > 0 && baton->height > 0) { if (baton->width > 0 && baton->height > 0) {
// Fixed width and height // Fixed width and height
xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width); xfactor = static_cast<double>(inputWidth) / (static_cast<double>(baton->width) + 0.1);
yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height); yfactor = static_cast<double>(inputHeight) / (static_cast<double>(baton->height) + 0.1);
switch (baton->canvas) { switch (baton->canvas) {
case Canvas::CROP: case Canvas::CROP:
xfactor = std::min(xfactor, yfactor); xfactor = std::min(xfactor, yfactor);
@@ -252,7 +259,7 @@ class PipelineWorker : public AsyncWorker {
} }
} else if (baton->width > 0) { } else if (baton->width > 0) {
// Fixed width // Fixed width
xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width); xfactor = static_cast<double>(inputWidth) / (static_cast<double>(baton->width) + 0.1);
if (baton->canvas == Canvas::IGNORE_ASPECT) { if (baton->canvas == Canvas::IGNORE_ASPECT) {
baton->height = inputHeight; baton->height = inputHeight;
} else { } else {
@@ -262,7 +269,7 @@ class PipelineWorker : public AsyncWorker {
} }
} else if (baton->height > 0) { } else if (baton->height > 0) {
// Fixed height // Fixed height
yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height); yfactor = static_cast<double>(inputHeight) / (static_cast<double>(baton->height) + 0.1);
if (baton->canvas == Canvas::IGNORE_ASPECT) { if (baton->canvas == Canvas::IGNORE_ASPECT) {
baton->width = inputWidth; baton->width = inputWidth;
} else { } else {
@@ -277,23 +284,23 @@ class PipelineWorker : public AsyncWorker {
} }
// Calculate integral box shrink // Calculate integral box shrink
int xshrink = CalculateShrink(xfactor, interpolatorWindowSize); int xshrink = std::max(1, static_cast<int>(floor(xfactor)));
int yshrink = CalculateShrink(yfactor, interpolatorWindowSize); int yshrink = std::max(1, static_cast<int>(floor(yfactor)));
// Calculate residual float affine transformation // Calculate residual float affine transformation
double xresidual = CalculateResidual(xshrink, xfactor); double xresidual = static_cast<double>(xshrink) / xfactor;
double yresidual = CalculateResidual(yshrink, yfactor); double yresidual = static_cast<double>(yshrink) / yfactor;
// Do not enlarge the output if the input width *or* height // Do not enlarge the output if the input width *or* height
// are already less than the required dimensions // are already less than the required dimensions
if (baton->withoutEnlargement) { if (baton->withoutEnlargement) {
if (inputWidth < baton->width || inputHeight < baton->height) { if (inputWidth < baton->width || inputHeight < baton->height) {
xfactor = 1; xfactor = 1.0;
yfactor = 1; yfactor = 1.0;
xshrink = 1; xshrink = 1;
yshrink = 1; yshrink = 1;
xresidual = 0; xresidual = 1.0;
yresidual = 0; yresidual = 1.0;
baton->width = inputWidth; baton->width = inputWidth;
baton->height = inputHeight; baton->height = inputHeight;
} }
@@ -303,7 +310,8 @@ class PipelineWorker : public AsyncWorker {
// but not when applying gamma correction or pre-resize extract // but not when applying gamma correction or pre-resize extract
int shrink_on_load = 1; int shrink_on_load = 1;
if ( if (
xshrink == yshrink && inputImageType == ImageType::JPEG && xshrink >= 2 && xshrink == yshrink && xshrink >= 2 &&
(inputImageType == ImageType::JPEG || inputImageType == ImageType::WEBP) &&
baton->gamma == 0 && baton->topOffsetPre == -1 baton->gamma == 0 && baton->topOffsetPre == -1
) { ) {
if (xshrink >= 8) { if (xshrink >= 8) {
@@ -324,20 +332,30 @@ class PipelineWorker : public AsyncWorker {
// Recalculate integral shrink and double residual // Recalculate integral shrink and double residual
xfactor = std::max(xfactor, 1.0); xfactor = std::max(xfactor, 1.0);
yfactor = std::max(yfactor, 1.0); yfactor = std::max(yfactor, 1.0);
xshrink = CalculateShrink(xfactor, interpolatorWindowSize); xshrink = std::max(1, static_cast<int>(floor(xfactor)));
yshrink = CalculateShrink(yfactor, interpolatorWindowSize); yshrink = std::max(1, static_cast<int>(floor(yfactor)));
xresidual = CalculateResidual(xshrink, xfactor); xresidual = static_cast<double>(xshrink) / xfactor;
yresidual = CalculateResidual(yshrink, yfactor); yresidual = static_cast<double>(yshrink) / yfactor;
// Reload input using shrink-on-load // Reload input using shrink-on-load
VOption *option = VImage::option()->set("shrink", shrink_on_load);
if (baton->bufferInLength > 1) { if (baton->bufferInLength > 1) {
VipsBlob *blob = vips_blob_new(nullptr, baton->bufferIn, baton->bufferInLength); VipsBlob *blob = vips_blob_new(nullptr, baton->bufferIn, baton->bufferInLength);
image = VImage::jpegload_buffer(blob, VImage::option()->set("shrink", shrink_on_load)); if (inputImageType == ImageType::JPEG) {
// Reload JPEG buffer
image = VImage::jpegload_buffer(blob, option);
} else {
// Reload WebP buffer
image = VImage::webpload_buffer(blob, option);
}
vips_area_unref(reinterpret_cast<VipsArea*>(blob)); vips_area_unref(reinterpret_cast<VipsArea*>(blob));
} else { } else {
image = VImage::jpegload( if (inputImageType == ImageType::JPEG) {
const_cast<char*>((baton->fileIn).data()), // Reload JPEG file
VImage::option()->set("shrink", shrink_on_load) image = VImage::jpegload(const_cast<char*>((baton->fileIn).data()), option);
); } else {
// Reload WebP file
image = VImage::webpload(const_cast<char*>((baton->fileIn).data()), option);
}
} }
} }
@@ -397,7 +415,12 @@ class PipelineWorker : public AsyncWorker {
} }
if (xshrink > 1 || yshrink > 1) { if (xshrink > 1 || yshrink > 1) {
image = image.shrink(xshrink, yshrink); if (yshrink > 1) {
image = image.shrinkv(yshrink);
}
if (xshrink > 1) {
image = image.shrinkh(xshrink);
}
// Recalculate residual float based on dimensions of required vs shrunk images // Recalculate residual float based on dimensions of required vs shrunk images
int shrunkWidth = image.width(); int shrunkWidth = image.width();
int shrunkHeight = image.height(); int shrunkHeight = image.height();
@@ -431,9 +454,9 @@ class PipelineWorker : public AsyncWorker {
); );
} }
bool shouldAffineTransform = xresidual != 0.0 || yresidual != 0.0; bool shouldAffineTransform = xresidual != 1.0 || yresidual != 1.0;
bool shouldBlur = baton->blurSigma != 0.0; bool shouldBlur = baton->blurSigma != 0.0;
bool shouldSharpen = baton->sharpenRadius != 0; bool shouldSharpen = baton->sharpenSigma != 0.0;
bool shouldThreshold = baton->threshold != 0; bool shouldThreshold = baton->threshold != 0;
bool shouldPremultiplyAlpha = HasAlpha(image) && bool shouldPremultiplyAlpha = HasAlpha(image) &&
(shouldAffineTransform || shouldBlur || shouldSharpen || hasOverlay); (shouldAffineTransform || shouldBlur || shouldSharpen || hasOverlay);
@@ -445,31 +468,41 @@ class PipelineWorker : public AsyncWorker {
image = image.premultiply(VImage::option()->set("max_alpha", maxAlpha)); image = image.premultiply(VImage::option()->set("max_alpha", maxAlpha));
} }
// Use affine transformation with the remaining float part // Use affine increase or kernel reduce with the remaining float part
if (shouldAffineTransform) { if (shouldAffineTransform) {
// Use average of x and y residuals to compute sigma for Gaussian blur // Insert tile cache to prevent over-computation of previous operations
double residual = (xresidual + yresidual) / 2.0; if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) {
// Apply Gaussian blur before large affine reductions image = TileCache(image, yresidual);
if (residual < 1.0) { }
// Calculate standard deviation // Perform kernel-based reduction
double sigma = ((1.0 / residual) - 0.4) / 3.0; if (yresidual < 1.0 || xresidual < 1.0) {
if (sigma >= 0.3) { VipsKernel kernel = static_cast<VipsKernel>(
// Sequential input requires a small linecache before use of convolution vips_enum_from_nick(nullptr, VIPS_TYPE_KERNEL, baton->kernel.data())
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) { );
image = image.linecache(VImage::option() if (kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 && kernel != VIPS_KERNEL_LANCZOS3) {
->set("access", VIPS_ACCESS_SEQUENTIAL) throw VError("Unknown kernel");
->set("tile_height", 1) }
->set("threaded", TRUE) if (yresidual < 1.0) {
); image = image.reducev(1.0 / yresidual, VImage::option()->set("kernel", kernel));
} }
// Apply Gaussian blur if (xresidual < 1.0) {
image = image.gaussblur(sigma); image = image.reduceh(1.0 / xresidual, VImage::option()->set("kernel", kernel));
}
}
// Perform affine enlargement
if (yresidual > 1.0 || xresidual > 1.0) {
VInterpolate interpolator = VInterpolate::new_from_name(baton->interpolator.data());
if (yresidual > 1.0) {
image = image.affine({1.0, 0.0, 0.0, yresidual}, VImage::option()
->set("interpolate", interpolator)
);
}
if (xresidual > 1.0) {
image = image.affine({xresidual, 0.0, 0.0, 1.0}, VImage::option()
->set("interpolate", interpolator)
);
} }
} }
// Perform affine transformation
image = image.affine({xresidual, 0.0, 0.0, yresidual}, VImage::option()
->set("interpolate", interpolator)
);
} }
// Rotate // Rotate
@@ -587,7 +620,7 @@ class PipelineWorker : public AsyncWorker {
// Sharpen // Sharpen
if (shouldSharpen) { if (shouldSharpen) {
image = Sharpen(image, baton->sharpenRadius, baton->sharpenFlat, baton->sharpenJagged); image = Sharpen(image, baton->sharpenSigma, baton->sharpenFlat, baton->sharpenJagged);
} }
// Composite with overlay, if present // Composite with overlay, if present
@@ -922,30 +955,6 @@ class PipelineWorker : public AsyncWorker {
return std::make_tuple(rotate, flip, flop); return std::make_tuple(rotate, flip, flop);
} }
/*
Calculate integral shrink given factor and interpolator window size
*/
int CalculateShrink(double factor, int interpolatorWindowSize) {
int shrink = 1;
if (factor >= 2.0 && trunc(factor) != factor && interpolatorWindowSize > 3) {
// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
shrink = static_cast<int>(floor(factor * 3.0 / interpolatorWindowSize));
} else {
shrink = static_cast<int>(floor(factor));
}
if (shrink < 1) {
shrink = 1;
}
return shrink;
}
/*
Calculate residual given shrink and factor
*/
double CalculateResidual(int shrink, double factor) {
return static_cast<double>(shrink) / factor;
}
/* /*
Clear all thread-local data. Clear all thread-local data.
*/ */
@@ -1037,12 +1046,13 @@ NAN_METHOD(pipeline) {
// Resize options // Resize options
baton->withoutEnlargement = attrAs<bool>(options, "withoutEnlargement"); baton->withoutEnlargement = attrAs<bool>(options, "withoutEnlargement");
baton->crop = attrAs<int32_t>(options, "crop"); baton->crop = attrAs<int32_t>(options, "crop");
baton->kernel = attrAsStr(options, "kernel");
baton->interpolator = attrAsStr(options, "interpolator"); baton->interpolator = attrAsStr(options, "interpolator");
// Operators // Operators
baton->flatten = attrAs<bool>(options, "flatten"); baton->flatten = attrAs<bool>(options, "flatten");
baton->negate = attrAs<bool>(options, "negate"); baton->negate = attrAs<bool>(options, "negate");
baton->blurSigma = attrAs<double>(options, "blurSigma"); baton->blurSigma = attrAs<double>(options, "blurSigma");
baton->sharpenRadius = attrAs<int32_t>(options, "sharpenRadius"); baton->sharpenSigma = attrAs<double>(options, "sharpenSigma");
baton->sharpenFlat = attrAs<double>(options, "sharpenFlat"); baton->sharpenFlat = attrAs<double>(options, "sharpenFlat");
baton->sharpenJagged = attrAs<double>(options, "sharpenJagged"); baton->sharpenJagged = attrAs<double>(options, "sharpenJagged");
baton->threshold = attrAs<int32_t>(options, "threshold"); baton->threshold = attrAs<int32_t>(options, "threshold");

View File

@@ -46,12 +46,13 @@ struct PipelineBaton {
int channels; int channels;
Canvas canvas; Canvas canvas;
int crop; int crop;
std::string kernel;
std::string interpolator; std::string interpolator;
double background[4]; double background[4];
bool flatten; bool flatten;
bool negate; bool negate;
double blurSigma; double blurSigma;
int sharpenRadius; double sharpenSigma;
double sharpenFlat; double sharpenFlat;
double sharpenJagged; double sharpenJagged;
int threshold; int threshold;
@@ -104,7 +105,7 @@ struct PipelineBaton {
flatten(false), flatten(false),
negate(false), negate(false),
blurSigma(0.0), blurSigma(0.0),
sharpenRadius(0), sharpenSigma(0.0),
sharpenFlat(1.0), sharpenFlat(1.0),
sharpenJagged(2.0), sharpenJagged(2.0),
threshold(0), threshold(0),

View File

@@ -138,7 +138,9 @@ NAN_METHOD(format) {
// Which load/save operations are available for each compressed format? // Which load/save operations are available for each compressed format?
Local<Object> format = New<Object>(); Local<Object> format = New<Object>();
for (std::string f : {"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz"}) { for (std::string f : {
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz", "ppm", "fits", "gif", "svg", "pdf"
}) {
// Input // Input
Local<Boolean> hasInputFile = Local<Boolean> hasInputFile =
New<Boolean>(vips_type_find("VipsOperation", (f + "load").c_str())); New<Boolean>(vips_type_find("VipsOperation", (f + "load").c_str()));

View File

@@ -10,11 +10,11 @@
"devDependencies": { "devDependencies": {
"async": "^1.5.2", "async": "^1.5.2",
"benchmark": "^2.1.0", "benchmark": "^2.1.0",
"gm": "^1.21.0", "gm": "^1.22.0",
"imagemagick": "^0.1.3", "imagemagick": "^0.1.3",
"imagemagick-native": "^1.9.2", "imagemagick-native": "^1.9.2",
"jimp": "^0.2.20", "jimp": "^0.2.24",
"lwip": "^0.0.8", "lwip": "^0.0.9",
"semver": "^5.1.0" "semver": "^5.1.0"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@@ -30,18 +30,15 @@ var fixtures = require('../fixtures');
var width = 720; var width = 720;
var height = 480; var height = 480;
var magickFilterBilinear = 'Triangle';
var magickFilterBicubic = 'Lanczos';
// Disable libvips cache to ensure tests are as fair as they can be // Disable libvips cache to ensure tests are as fair as they can be
sharp.cache(false); sharp.cache(false);
// Enable use of SIMD // Enable use of SIMD
sharp.simd(true); sharp.simd(true);
async.series({ async.series({
'jpeg-linear': function(callback) { 'jpeg': function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg); var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var jpegSuite = new Benchmark.Suite('jpeg-linear'); var jpegSuite = new Benchmark.Suite('jpeg');
// jimp // jimp
jpegSuite.add('jimp-buffer-buffer', { jpegSuite.add('jimp-buffer-buffer', {
defer: true, defer: true,
@@ -93,7 +90,7 @@ async.series({
if (err) { if (err) {
throw err; throw err;
} }
image.resize(width, height, 'linear', function (err, image) { image.resize(width, height, 'lanczos', function (err, image) {
if (err) { if (err) {
throw err; throw err;
} }
@@ -113,7 +110,7 @@ async.series({
if (err) { if (err) {
throw err; throw err;
} }
image.resize(width, height, 'linear', function (err, image) { image.resize(width, height, 'lanczos', function (err, image) {
if (err) { if (err) {
throw err; throw err;
} }
@@ -140,7 +137,7 @@ async.series({
width: width, width: width,
height: height, height: height,
format: 'jpg', format: 'jpg',
filter: magickFilterBilinear filter: 'Lanczos'
}, function(err) { }, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -161,7 +158,7 @@ async.series({
width: width, width: width,
height: height, height: height,
format: 'JPEG', format: 'JPEG',
filter: magickFilterBilinear filter: 'Lanczos'
}, function (err, buffer) { }, function (err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -179,7 +176,7 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
gm(inputJpgBuffer) gm(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.filter(magickFilterBilinear) .filter('Lanczos')
.quality(80) .quality(80)
.write(fixtures.outputJpg, function (err) { .write(fixtures.outputJpg, function (err) {
if (err) { if (err) {
@@ -194,7 +191,7 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
gm(inputJpgBuffer) gm(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.filter(magickFilterBilinear) .filter('Lanczos')
.quality(80) .quality(80)
.toBuffer(function (err, buffer) { .toBuffer(function (err, buffer) {
if (err) { if (err) {
@@ -210,7 +207,7 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
gm(fixtures.inputJpg) gm(fixtures.inputJpg)
.resize(width, height) .resize(width, height)
.filter(magickFilterBilinear) .filter('Lanczos')
.quality(80) .quality(80)
.write(fixtures.outputJpg, function (err) { .write(fixtures.outputJpg, function (err) {
if (err) { if (err) {
@@ -225,7 +222,7 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
gm(fixtures.inputJpg) gm(fixtures.inputJpg)
.resize(width, height) .resize(width, height)
.filter(magickFilterBilinear) .filter('Lanczos')
.quality(80) .quality(80)
.toBuffer(function (err, buffer) { .toBuffer(function (err, buffer) {
if (err) { if (err) {
@@ -243,7 +240,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputJpg, function(err) { .toFile(fixtures.outputJpg, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -257,7 +253,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -272,7 +267,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputJpg, function(err) { .toFile(fixtures.outputJpg, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -290,8 +284,7 @@ async.series({
deferred.resolve(); deferred.resolve();
}); });
var pipeline = sharp() var pipeline = sharp()
.resize(width, height) .resize(width, height);
.interpolateWith(sharp.interpolator.bilinear);
readable.pipe(pipeline).pipe(writable); readable.pipe(pipeline).pipe(writable);
} }
}).add('sharp-file-buffer', { }).add('sharp-file-buffer', {
@@ -299,7 +292,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -314,19 +306,27 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer() .toBuffer()
.then(function(buffer) { .then(function(buffer) {
assert.notStrictEqual(null, buffer); assert.notStrictEqual(null, buffer);
deferred.resolve(); deferred.resolve();
}); });
} }
}).add('sharp-sharpen-mild', { }).on('cycle', function(event) {
console.log('jpeg ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').map('name'));
}).run();
},
// Effect of applying operations
operations: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var operationsSuite = new Benchmark.Suite('operations');
operationsSuite.add('sharp-sharpen-mild', {
defer: true, defer: true,
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sharpen() .sharpen()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -342,7 +342,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sharpen(3, 1, 3) .sharpen(3, 1, 3)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -358,7 +357,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.blur() .blur()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -374,7 +372,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.blur(3) .blur(3)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -390,7 +387,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.gamma() .gamma()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -406,7 +402,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.normalise() .normalise()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -422,7 +417,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.greyscale() .greyscale()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -438,7 +432,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.gamma() .gamma()
.greyscale() .greyscale()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
@@ -455,7 +448,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.progressive() .progressive()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -471,7 +463,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.withoutChromaSubsampling() .withoutChromaSubsampling()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -487,7 +478,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.rotate(90) .rotate(90)
.interpolateWith(sharp.interpolator.bilinear)
.resize(width, height) .resize(width, height)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -503,8 +493,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp.simd(false); sharp.simd(false);
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.rotate(90)
.interpolateWith(sharp.interpolator.bilinear)
.resize(width, height) .resize(width, height)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
sharp.simd(true); sharp.simd(true);
@@ -520,9 +508,8 @@ async.series({
defer: true, defer: true,
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sequentialRead() .sequentialRead()
.resize(width, height)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -533,189 +520,19 @@ async.series({
}); });
} }
}).on('cycle', function(event) { }).on('cycle', function(event) {
console.log('jpeg-linear ' + String(event.target)); console.log('operations ' + String(event.target));
}).on('complete', function() { }).on('complete', function() {
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
// Comparitive speed of kernels
'jpeg-cubic': function(callback) { kernels: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg); var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var jpegSuite = new Benchmark.Suite('jpeg-cubic'); (new Benchmark.Suite('kernels')).add('sharp-cubic', {
// lwip
if (typeof lwip !== 'undefined') {
jpegSuite.add('lwip-file-file', {
defer: true,
fn: function(deferred) {
lwip.open(fixtures.inputJpg, function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.writeFile(fixtures.outputJpg, {quality: 80}, function (err) {
if (err) {
throw err;
}
deferred.resolve();
});
});
});
}
}).add('lwip-buffer-buffer', {
defer: true,
fn: function(deferred) {
lwip.open(inputJpgBuffer, 'jpg', function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.toBuffer('jpg', {quality: 80}, function (err, buffer) {
if (err) {
throw err;
}
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
});
});
}
});
}
// imagemagick
jpegSuite.add('imagemagick-file-file', {
defer: true,
fn: function(deferred) {
imagemagick.resize({
srcPath: fixtures.inputJpg,
dstPath: fixtures.outputJpg,
quality: 0.8,
width: width,
height: height,
format: 'jpg',
filter: magickFilterBicubic
}, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
// imagemagick-native
if (typeof imagemagickNative !== 'undefined') {
jpegSuite.add('imagemagick-native-buffer-buffer', {
defer: true,
fn: function(deferred) {
imagemagickNative.convert({
srcData: inputJpgBuffer,
quality: 80,
width: width,
height: height,
format: 'JPEG',
filter: magickFilterBicubic
}, function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
}
// gm
jpegSuite.add('gm-buffer-file', {
defer: true,
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('gm-buffer-buffer', {
defer: true,
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('gm-file-file', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('gm-file-buffer', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
// sharp
jpegSuite.add('sharp-buffer-file', {
defer: true, defer: true,
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height, { kernel: 'cubic' })
.interpolateWith(sharp.interpolator.bicubic)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-buffer-buffer', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -725,39 +542,11 @@ async.series({
} }
}); });
} }
}).add('sharp-file-file', { }).add('sharp-lanczos2', {
defer: true, defer: true,
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputJpg) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height, { kernel: 'lanczos2' })
.interpolateWith(sharp.interpolator.bicubic)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-stream-stream', {
defer: true,
fn: function(deferred) {
var readable = fs.createReadStream(fixtures.inputJpg);
var writable = fs.createWriteStream(fixtures.outputJpg);
writable.on('finish', function() {
deferred.resolve();
});
var pipeline = sharp()
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic);
readable.pipe(pipeline).pipe(writable);
}
}).add('sharp-file-buffer', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -767,109 +556,11 @@ async.series({
} }
}); });
} }
}).add('sharp-promise', { }).add('sharp-lanczos3', {
defer: true, defer: true,
fn: function(deferred) { fn: function(deferred) {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height, { kernel: 'lanczos3' })
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer()
.then(function(buffer) {
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
}
}).on('cycle', function(event) {
console.log('jpeg-cubic ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').map('name'));
}).run();
},
// Comparitive speed of pixel interpolators
interpolators: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
(new Benchmark.Suite('interpolators')).add('sharp-nearest-neighbour', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.nearest)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-bilinear', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-vertexSplitQuadraticBasisSpline', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-bicubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-locallyBoundedBicubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.locallyBoundedBicubic)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-nohalo', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.nohalo)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -880,12 +571,12 @@ async.series({
}); });
} }
}).on('cycle', function(event) { }).on('cycle', function(event) {
console.log('interpolators ' + String(event.target)); console.log('kernels ' + String(event.target));
}).on('complete', function() { }).on('complete', function() {
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
// PNG
png: function(callback) { png: function(callback) {
var inputPngBuffer = fs.readFileSync(fixtures.inputPng); var inputPngBuffer = fs.readFileSync(fixtures.inputPng);
var pngSuite = new Benchmark.Suite('png'); var pngSuite = new Benchmark.Suite('png');
@@ -938,7 +629,7 @@ async.series({
if (err) { if (err) {
throw err; throw err;
} }
image.resize(width, height, 'linear', function (err, image) { image.resize(width, height, 'lanczos', function (err, image) {
if (err) { if (err) {
throw err; throw err;
} }
@@ -963,7 +654,7 @@ async.series({
dstPath: fixtures.outputPng, dstPath: fixtures.outputPng,
width: width, width: width,
height: height, height: height,
filter: magickFilterBilinear filter: 'Lanczos'
}, function(err) { }, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -983,7 +674,7 @@ async.series({
width: width, width: width,
height: height, height: height,
format: 'PNG', format: 'PNG',
filter: magickFilterBilinear filter: 'Lanczos'
}); });
deferred.resolve(); deferred.resolve();
} }
@@ -995,7 +686,7 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
gm(fixtures.inputPng) gm(fixtures.inputPng)
.resize(width, height) .resize(width, height)
.filter(magickFilterBilinear) .filter('Lanczos')
.write(fixtures.outputPng, function (err) { .write(fixtures.outputPng, function (err) {
if (err) { if (err) {
throw err; throw err;
@@ -1009,7 +700,7 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
gm(fixtures.inputPng) gm(fixtures.inputPng)
.resize(width, height) .resize(width, height)
.filter(magickFilterBilinear) .filter('Lanczos')
.toBuffer(function (err, buffer) { .toBuffer(function (err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -1026,7 +717,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputPng, function(err) { .toFile(fixtures.outputPng, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -1040,7 +730,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -1055,7 +744,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputPng) sharp(fixtures.inputPng)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputPng, function(err) { .toFile(fixtures.outputPng, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -1069,7 +757,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputPng) sharp(fixtures.inputPng)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -1084,7 +771,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.progressive() .progressive()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -1100,7 +786,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.withoutAdaptiveFiltering() .withoutAdaptiveFiltering()
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
@@ -1118,7 +803,7 @@ async.series({
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
// WebP
webp: function(callback) { webp: function(callback) {
var inputWebPBuffer = fs.readFileSync(fixtures.inputWebP); var inputWebPBuffer = fs.readFileSync(fixtures.inputWebP);
(new Benchmark.Suite('webp')).add('sharp-buffer-file', { (new Benchmark.Suite('webp')).add('sharp-buffer-file', {
@@ -1126,7 +811,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputWebPBuffer) sharp(inputWebPBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputWebP, function(err) { .toFile(fixtures.outputWebP, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -1140,7 +824,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(inputWebPBuffer) sharp(inputWebPBuffer)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;
@@ -1155,7 +838,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputWebP) sharp(fixtures.inputWebP)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputWebP, function(err) { .toFile(fixtures.outputWebP, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -1169,7 +851,6 @@ async.series({
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputWebp) sharp(fixtures.inputWebp)
.resize(width, height) .resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) { .toBuffer(function(err, buffer) {
if (err) { if (err) {
throw err; throw err;

View File

@@ -8,14 +8,12 @@ var Benchmark = require('benchmark');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(false);
sharp.simd(true); sharp.simd(true);
var min = 320; var min = 320;
var max = 960; var max = 960;
// Nearest equivalent to bilinear
var magickFilter = 'Triangle';
var randomDimension = function() { var randomDimension = function() {
return Math.ceil(Math.random() * (max - min) + min); return Math.ceil(Math.random() * (max - min) + min);
}; };
@@ -30,7 +28,7 @@ new Benchmark.Suite('random').add('imagemagick', {
width: randomDimension(), width: randomDimension(),
height: randomDimension(), height: randomDimension(),
format: 'jpg', format: 'jpg',
filter: magickFilter filter: 'Lanczos'
}, function(err) { }, function(err) {
if (err) { if (err) {
throw err; throw err;
@@ -44,7 +42,7 @@ new Benchmark.Suite('random').add('imagemagick', {
fn: function(deferred) { fn: function(deferred) {
gm(fixtures.inputJpg) gm(fixtures.inputJpg)
.resize(randomDimension(), randomDimension()) .resize(randomDimension(), randomDimension())
.filter(magickFilter) .filter('Lanczos')
.quality(80) .quality(80)
.toBuffer(function (err, buffer) { .toBuffer(function (err, buffer) {
if (err) { if (err) {
@@ -58,19 +56,20 @@ new Benchmark.Suite('random').add('imagemagick', {
}).add('sharp', { }).add('sharp', {
defer: true, defer: true,
fn: function(deferred) { fn: function(deferred) {
sharp(fixtures.inputJpg).resize(randomDimension(), randomDimension()).toBuffer(function(err, buffer) { sharp(fixtures.inputJpg)
if (err) { .resize(randomDimension(), randomDimension())
throw err; .toBuffer(function(err, buffer) {
} else { if (err) {
assert.notStrictEqual(null, buffer); throw err;
deferred.resolve(); } else {
} assert.notStrictEqual(null, buffer);
}); deferred.resolve();
}
});
} }
}).on('cycle', function(event) { }).on('cycle', function(event) {
console.log(String(event.target)); console.log(String(event.target));
}).on('complete', function() { }).on('complete', function() {
var winner = this.filter('fastest').map('name'); var winner = this.filter('fastest').map('name');
assert.strictEqual('sharp', String(winner), 'sharp was slower than ' + winner); assert.strictEqual('sharp', String(winner), 'sharp was slower than ' + winner);
console.dir(sharp.cache());
}).run(); }).run();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 827 B

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 B

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 938 B

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

BIN
test/fixtures/grey-plus-alpha.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

View File

@@ -18,7 +18,6 @@ var fingerprint = function(image, callback) {
.normalise() .normalise()
.resize(9, 8) .resize(9, 8)
.ignoreAspectRatio() .ignoreAspectRatio()
.interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline)
.raw() .raw()
.toBuffer(function(err, data) { .toBuffer(function(err, data) {
if (err) { if (err) {
@@ -85,8 +84,8 @@ module.exports = {
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
inputPsd: getPath('free-gearhead-pack.psd'), // https://dribbble.com/shots/1624241-Free-Gearhead-Vector-Pack
inputSvs: getPath('CMU-1-Small-Region.svs'), // http://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/CMU-1-Small-Region.svs inputSvs: getPath('CMU-1-Small-Region.svs'), // http://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/CMU-1-Small-Region.svs

View File

@@ -1,48 +0,0 @@
# Interpolators
[Photo](https://www.flickr.com/photos/aotaro/21978966091) by
[aotaro](https://www.flickr.com/photos/aotaro/) is licensed under
[CC BY 2.0](https://creativecommons.org/licenses/by/2.0/).
The following examples take the 4608x3072px original image
and resize to 480x320px using various interpolators.
To fetch the original 4608x3072px image and
generate the interpolator sample images:
```sh
curl -O https://farm6.staticflickr.com/5682/21978966091_b421afe866_o.jpg
node generate.js
```
## Nearest neighbour
![Nearest neighbour interpolation](nearest.jpg)
## Bilinear
![Bilinear interpolation](bilinear.jpg)
## Bicubic
![Bicubic interpolation](bicubic.jpg)
## Locally bounded bicubic
![Locally bounded bicubic interpolation](lbb.jpg)
## Vertex-split quadratic b-splines (VSQBS)
![Vertex-split quadratic b-splines interpolation](vsqbs.jpg)
## Nohalo
![Nohalo interpolation](nohalo.jpg)
## GraphicsMagick
![GraphicsMagick](gm.jpg)
```sh
gm convert 21978966091_b421afe866_o.jpg -resize 480x320^ -gravity center -extent 480x320 -quality 95 -strip -define jpeg:optimize-coding=true gm.jpg
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -1,11 +0,0 @@
'use strict';
['nearest', 'bilinear', 'bicubic', 'vsqbs', 'lbb', 'nohalo'].forEach(function(interpolator) {
require('../../')('21978966091_b421afe866_o.jpg')
.resize(480, 320)
.interpolateWith(interpolator)
.quality(95)
.toFile(interpolator + '.jpg', function(err) {
if (err) throw err;
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -83,6 +83,54 @@
... ...
fun:WebPEncodeRGB fun:WebPEncodeRGB
} }
{
value_libwebp_WebPEncode
Memcheck:Value8
...
fun:WebPEncode
}
{
cond_libwebp_WebPEncode
Memcheck:Cond
...
fun:WebPEncode
}
{
value_libwebp_WebPPictureImportRGBA
Memcheck:Value8
...
fun:WebPPictureImportRGBA
}
{
cond_libwebp_WebPPictureImportRGBA
Memcheck:Cond
...
fun:WebPPictureImportRGBA
}
{
value_libwebp_WebPPictureImportRGB
Memcheck:Value8
...
fun:WebPPictureImportRGB
}
{
cond_libwebp_WebPPictureImportRGB
Memcheck:Cond
...
fun:WebPPictureImportRGB
}
{
value_libwebp_WebPDecode
Memcheck:Value8
...
fun:WebPDecode
}
{
cond_libwebp_WebPDecode
Memcheck:Cond
...
fun:WebPDecode
}
# libvips # libvips
{ {
@@ -102,7 +150,21 @@
cond_libvips_col_sRGB2scRGB_8 cond_libvips_col_sRGB2scRGB_8
Memcheck:Value8 Memcheck:Value8
fun:vips_col_sRGB2scRGB_8 fun:vips_col_sRGB2scRGB_8
fun:vips_sRGB2scRGB_gen }
{
cond_libvips_vips_region_fill
Memcheck:Cond
...
fun:vips_region_fill
fun:vips_region_prepare
}
{
leak_libvips_init
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
...
fun:vips__init
} }
# libuv warnings # libuv warnings
@@ -199,6 +261,13 @@
... ...
fun:_ZN4node17CreateEnvironmentEPN2v87IsolateEP9uv_loop_sNS0_5LocalINS0_7ContextEEEiPKPKciSB_ fun:_ZN4node17CreateEnvironmentEPN2v87IsolateEP9uv_loop_sNS0_5LocalINS0_7ContextEEEiPKPKciSB_
} }
{
leak_nodejs_icu_getAvailableLocales
Memcheck:Leak
match-leak-kinds: possible
...
fun:_ZN6icu_568Collator19getAvailableLocalesERi
}
{ {
leak_nan_FunctionCallbackInfo leak_nan_FunctionCallbackInfo
Memcheck:Leak Memcheck:Leak
@@ -276,55 +345,3 @@
... ...
fun:_ZN2v88internal8Malloced3NewEm fun:_ZN2v88internal8Malloced3NewEm
} }
# vips__init warnings
{
leak_libvips_init
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
...
fun:vips__init
}
# *magick warnings
{
leak_magick_read
Memcheck:Leak
match-leak-kinds: definite,indirect,possible
...
fun:ReadImage
...
fun:vips__magick_read
}
{
leak_magick_read_header
Memcheck:Leak
match-leak-kinds: definite,indirect,possible
...
fun:ReadImage
...
fun:vips__magick_read_header
}
{
value_magick_is_palette_image
Memcheck:Value8
fun:IsPaletteImage
}
{
cond_magick_is_palette_image
Memcheck:Cond
fun:IsPaletteImage
}
# glib g_file_read_link
# https://github.com/GNOME/glib/commit/49a5d0f6f2aed99cd78f25655f137f4448e47d92
{
leak_g_file_read_link
Memcheck:Leak
match-leak-kinds: definite,indirect,possible
...
fun:g_file_read_link
...
fun:vips_gsf_path
}

View File

@@ -45,15 +45,17 @@ describe('Alpha transparency', function() {
}); });
it('Flatten 16-bit PNG with transparency to orange', function(done) { it('Flatten 16-bit PNG with transparency to orange', function(done) {
var output = fixtures.path('output.flatten-rgb16-orange.jpg');
sharp(fixtures.inputPngWithTransparency16bit) sharp(fixtures.inputPngWithTransparency16bit)
.flatten() .flatten()
.background({r: 255, g: 102, b: 0}) .background({r: 255, g: 102, b: 0})
.toBuffer(function(err, data, info) { .toFile(output, function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0); assert.strictEqual(true, info.size > 0);
assert.strictEqual(32, info.width); assert.strictEqual(32, info.width);
assert.strictEqual(32, info.height); assert.strictEqual(32, info.height);
fixtures.assertSimilar(fixtures.expected('flatten-rgb16-orange.jpg'), data, { threshold: 6 }, done); fixtures.assertMaxColourDistance(output, fixtures.expected('flatten-rgb16-orange.jpg'), 25);
done();
}); });
}); });

View File

@@ -57,7 +57,7 @@ describe('Clone', function() {
var rotator = sharp().rotate(90); var rotator = sharp().rotate(90);
// Cloned instances with differing dimensions // Cloned instances with differing dimensions
rotator.clone().resize(320, 240).pipe(writable1); rotator.clone().resize(320, 240).pipe(writable1);
rotator.clone().resize(100).pipe(writable2); rotator.clone().resize(100, 122).pipe(writable2);
// Go // Go
fs.createReadStream(fixtures.inputJpg).pipe(rotator); fs.createReadStream(fixtures.inputJpg).pipe(rotator);
}); });

View File

@@ -5,100 +5,67 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
describe('Interpolation', function() { describe('Interpolators and kernels', function() {
it('nearest neighbour', function(done) { describe('Reducers', function() {
sharp(fixtures.inputJpg) [
.resize(320, 240) sharp.kernel.cubic,
.interpolateWith(sharp.interpolator.nearest) sharp.kernel.lanczos2,
.toBuffer(function(err, data, info) { sharp.kernel.lanczos3
if (err) throw err; ].forEach(function(kernel) {
assert.strictEqual(true, data.length > 0); it(kernel, function(done) {
assert.strictEqual('jpeg', info.format); sharp(fixtures.inputJpg)
assert.strictEqual(320, info.width); .resize(320, null, { kernel: kernel })
assert.strictEqual(240, info.height); .toBuffer(function(err, data, info) {
done(); if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
fixtures.assertSimilar(fixtures.inputJpg, data, done);
});
}); });
});
}); });
it('bilinear', function(done) { describe('Enlargers', function() {
sharp(fixtures.inputJpg) [
.resize(320, 240) sharp.interpolator.nearest,
.interpolateWith(sharp.interpolator.bilinear) sharp.interpolator.bilinear,
.toBuffer(function(err, data, info) { sharp.interpolator.bicubic,
if (err) throw err; sharp.interpolator.nohalo,
assert.strictEqual(true, data.length > 0); sharp.interpolator.locallyBoundedBicubic,
assert.strictEqual('jpeg', info.format); sharp.interpolator.vertexSplitQuadraticBasisSpline
assert.strictEqual(320, info.width); ].forEach(function(interpolator) {
assert.strictEqual(240, info.height); it(interpolator, function(done) {
done(); sharp(fixtures.inputJpg)
.resize(320, null, { interpolator: interpolator })
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
fixtures.assertSimilar(fixtures.inputJpg, data, done);
});
}); });
});
}); });
it('bicubic', function(done) { it('unknown kernel throws', function() {
sharp(fixtures.inputJpg) assert.throws(function() {
.resize(320, 240) sharp().resize(null, null, { kernel: 'unknown' });
.interpolateWith(sharp.interpolator.bicubic) });
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
}); });
it('nohalo', function(done) { it('unknown interpolator throws', function() {
sharp(fixtures.inputJpg) assert.throws(function() {
.resize(320, 240) sharp().resize(null, null, { interpolator: 'unknown' });
.interpolateWith(sharp.interpolator.nohalo) });
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
}); });
it('locally bounded bicubic (LBB)', function(done) { describe('deprecated interpolateWith method still works', function() {
sharp(fixtures.inputJpg) it('resize then interpolateWith', function() {
.resize(320, 240) sharp().resize(1, 1).interpolateWith('bicubic');
.interpolateWith(sharp.interpolator.locallyBoundedBicubic) });
.toBuffer(function(err, data, info) { it('interpolateWith then resize', function() {
if (err) throw err; sharp().interpolateWith('bicubic').resize(1, 1);
assert.strictEqual(true, data.length > 0); });
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
}); });
it('vertex split quadratic basis spline (VSQBS)', function(done) {
sharp(fixtures.inputJpg)
.resize(320, 240)
.interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline)
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
});
it('unknown interpolator throws', function(done) {
var isValid = false;
try {
sharp().interpolateWith('nonexistant');
isValid = true;
} catch (e) {}
assert(!isValid);
done();
});
}); });

View File

@@ -668,29 +668,25 @@ describe('Input/output', function() {
}); });
}); });
if (sharp.format.magick.input.file) { if (sharp.format.svg.input.file) {
it('Convert SVG to PNG at default 72DPI', function(done) { it('Convert SVG to PNG at default 72DPI', function(done) {
sharp(fixtures.inputSvg) sharp(fixtures.inputSvg)
.resize(1024) .resize(1024)
.extract({left: 290, top: 760, width: 40, height: 40}) .extract({left: 290, top: 760, width: 40, height: 40})
.toFormat('png') .toFormat('png')
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
if (err) { if (err) throw err;
assert.strictEqual(0, err.message.indexOf('Input file is missing or of an unsupported image format')); assert.strictEqual('png', info.format);
done(); assert.strictEqual(40, info.width);
} else { assert.strictEqual(40, info.height);
assert.strictEqual('png', info.format); fixtures.assertSimilar(fixtures.expected('svg72.png'), data, function(err) {
assert.strictEqual(40, info.width); if (err) throw err;
assert.strictEqual(40, info.height); sharp(data).metadata(function(err, info) {
fixtures.assertSimilar(fixtures.expected('svg72.png'), data, function(err) {
if (err) throw err; if (err) throw err;
sharp(data).metadata(function(err, info) { assert.strictEqual(72, info.density);
if (err) throw err; done();
assert.strictEqual(72, info.density);
done();
});
}); });
} });
}); });
}); });
it('Convert SVG to PNG at 300DPI', function(done) { it('Convert SVG to PNG at 300DPI', function(done) {
@@ -699,38 +695,18 @@ describe('Input/output', function() {
.extract({left: 290, top: 760, width: 40, height: 40}) .extract({left: 290, top: 760, width: 40, height: 40})
.toFormat('png') .toFormat('png')
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
if (err) {
assert.strictEqual(0, err.message.indexOf('Input file is missing or of an unsupported image format'));
done();
} else {
assert.strictEqual('png', info.format);
assert.strictEqual(40, info.width);
assert.strictEqual(40, info.height);
fixtures.assertSimilar(fixtures.expected('svg1200.png'), data, function(err) {
if (err) throw err;
sharp(data).metadata(function(err, info) {
if (err) throw err;
assert.strictEqual(1200, info.density);
done();
});
});
}
});
});
}
if (sharp.format.magick.input.file) {
it('Convert PSD to PNG', function(done) {
sharp(fixtures.inputPsd)
.resize(320, 240)
.toFormat(sharp.format.png)
.toFile(fixtures.path('output.psd.png'), function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(40, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(40, info.height);
done(); fixtures.assertSimilar(fixtures.expected('svg1200.png'), data, function(err) {
if (err) throw err;
sharp(data).metadata(function(err, info) {
if (err) throw err;
assert.strictEqual(1200, info.density);
done();
});
});
}); });
}); });
} }
@@ -753,7 +729,7 @@ describe('Input/output', function() {
}); });
} }
if (sharp.format.magick.input.buffer) { if (sharp.format.gif.input.buffer) {
it('Load GIF from Buffer', function(done) { it('Load GIF from Buffer', function(done) {
var inputGifBuffer = fs.readFileSync(fixtures.inputGif); var inputGifBuffer = fs.readFileSync(fixtures.inputGif);
sharp(inputGifBuffer) sharp(inputGifBuffer)
@@ -771,6 +747,24 @@ describe('Input/output', function() {
}); });
} }
if (sharp.format.gif.input.file) {
it('Load GIF grey+alpha from file', function(done) {
sharp(fixtures.inputGifGreyPlusAlpha)
.resize(8, 4)
.png()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('png', info.format);
assert.strictEqual(8, info.width);
assert.strictEqual(4, info.height);
assert.strictEqual(4, info.channels);
done();
});
});
}
if (sharp.format.openslide.input.file) { if (sharp.format.openslide.input.file) {
it('Load Aperio SVS file via Openslide', function(done) { it('Load Aperio SVS file via Openslide', function(done) {
sharp(fixtures.inputSvs) sharp(fixtures.inputSvs)
@@ -1034,14 +1028,14 @@ describe('Input/output', function() {
it('Info event data', function(done) { it('Info event data', function(done) {
var readable = fs.createReadStream(fixtures.inputJPGBig); var readable = fs.createReadStream(fixtures.inputJPGBig);
var inPipeline = sharp() var inPipeline = sharp()
.resize(840) .resize(840, 472)
.raw() .raw()
.on('info', function(info) { .on('info', function(info) {
assert.strictEqual(840, info.width); assert.strictEqual(840, info.width);
assert.strictEqual(472, info.height); assert.strictEqual(472, info.height);
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
}); });
var badPipeline = sharp(null, {raw: {width: 840, height: 473, channels: 3}}) var badPipeline = sharp(null, {raw: {width: 840, height: 500, channels: 3}})
.toFormat('jpeg') .toFormat('jpeg')
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
assert.strictEqual(err.message.indexOf('memory area too small') > 0, true); assert.strictEqual(err.message.indexOf('memory area too small') > 0, true);
@@ -1053,7 +1047,7 @@ describe('Input/output', function() {
done(); done();
}); });
inPipeline = sharp() inPipeline = sharp()
.resize(840) .resize(840, 472)
.raw(); .raw();
readable.pipe(inPipeline).pipe(goodPipeline); readable.pipe(inPipeline).pipe(goodPipeline);
}); });

View File

@@ -133,17 +133,33 @@ describe('Image metadata', function() {
}); });
} }
if (sharp.format.magick.input.file) { if (sharp.format.gif.input.file) {
it('GIF via libmagick', function(done) { it('GIF via giflib', function(done) {
sharp(fixtures.inputGif).metadata(function(err, metadata) { sharp(fixtures.inputGif).metadata(function(err, metadata) {
if (err) throw err; if (err) throw err;
assert.strictEqual('magick', metadata.format); assert.strictEqual('gif', metadata.format);
assert.strictEqual(800, metadata.width); assert.strictEqual(800, metadata.width);
assert.strictEqual(533, metadata.height); assert.strictEqual(533, metadata.height);
assert.strictEqual(3, metadata.channels); assert.strictEqual(4, metadata.channels);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(true, metadata.hasAlpha);
assert.strictEqual('undefined', typeof metadata.orientation);
assert.strictEqual('undefined', typeof metadata.exif);
assert.strictEqual('undefined', typeof metadata.icc);
done();
});
});
it('GIF grey+alpha via giflib', function(done) {
sharp(fixtures.inputGifGreyPlusAlpha).metadata(function(err, metadata) {
if (err) throw err;
assert.strictEqual('gif', metadata.format);
assert.strictEqual(2, metadata.width);
assert.strictEqual(1, metadata.height);
assert.strictEqual(4, metadata.channels);
assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(true, metadata.hasAlpha);
assert.strictEqual('undefined', typeof metadata.orientation); assert.strictEqual('undefined', typeof metadata.orientation);
assert.strictEqual('undefined', typeof metadata.exif); assert.strictEqual('undefined', typeof metadata.exif);
assert.strictEqual('undefined', typeof metadata.icc); assert.strictEqual('undefined', typeof metadata.icc);

View File

@@ -7,10 +7,10 @@ var fixtures = require('../fixtures');
describe('Sharpen', function() { describe('Sharpen', function() {
it('specific radius 10', function(done) { it('specific radius 10 (sigma 6)', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.sharpen(10) .sharpen(6)
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@@ -19,10 +19,10 @@ describe('Sharpen', function() {
}); });
}); });
it('specific radius 3 and levels 0.5, 2.5', function(done) { it('specific radius 3 (sigma 1.5) and levels 0.5, 2.5', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.sharpen(3, 0.5, 2.5) .sharpen(1.5, 0.5, 2.5)
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@@ -31,10 +31,10 @@ describe('Sharpen', function() {
}); });
}); });
it('specific radius 5 and levels 2, 4', function(done) { it('specific radius 5 (sigma 3.5) and levels 2, 4', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.sharpen(5, 2, 4) .sharpen(3.5, 2, 4)
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@@ -55,9 +55,9 @@ describe('Sharpen', function() {
}); });
}); });
it('invalid radius', function() { it('invalid sigma', function() {
assert.throws(function() { assert.throws(function() {
sharp(fixtures.inputJpg).sharpen(1.5); sharp(fixtures.inputJpg).sharpen(-1.5);
}); });
}); });