Compare commits

..

36 Commits

Author SHA1 Message Date
Lovell Fuller
c210ac73cc Release v0.13.1 2016-02-27 16:17:58 +00:00
Lovell Fuller
962c91daf0 OpenSUSE package name for npm has changed 2016-02-25 22:19:08 +00:00
Lovell Fuller
df33c3024a Fix embedding onto transparent backgrounds #366
Fully automate embed tests to prevent regression
2016-02-25 18:36:00 +00:00
Lovell Fuller
62e04f7784 Merge pull request #361 from jardakotesovec/clone-clean-up
Remove left over, non-functional code from clone feature.
2016-02-17 11:38:34 +00:00
Jarda Kotesovec
32fcb771ca clone clean up 2016-02-17 10:25:00 +01:00
Lovell Fuller
a21760b374 Release v0.13.0 2016-02-15 19:12:23 +00:00
Lovell Fuller
cd05c7814a Merge pull request #359 from wjordan/alpine-packaging-test
Use libvips-dev apk for alpine-linux packaging test
2016-02-13 20:37:21 +00:00
Will Jordan
7b12f091e8 use alpine:edge image for packaging test
vips requires main/gettext-0.19.7r1 for aports commit 4ef70b432b05b720f7f144c2060550749d378205 to link correctly.
2016-02-13 13:48:46 -05:00
Will Jordan
e149e60c7a Use libvips-dev apk for alpine-linux packaging test 2016-02-13 16:07:56 +00:00
Lovell Fuller
bdac84059d Update perf results for forthcoming v0.13.0
Install runtime rather than dev pkgs on Alpine Linux
2016-02-12 19:33:21 +00:00
Lovell Fuller
2d05804fc3 Add cache recommendations for use with Alpine/musl #354
Prevent Windows EBUSY errors during tests
2016-02-11 20:33:33 +00:00
Lovell Fuller
2a56de69cc Add Alpine Linux packaging test #354
Requires libvips cache to be disabled for tests
Skip tiff/magick tests when format unavailable
2016-02-11 18:30:50 +00:00
Lovell Fuller
6ca2a4a9cd Ensure sharp.format lists support for raw input #220 2016-02-11 18:12:51 +00:00
Lovell Fuller
a9eb65c462 Most Linux systems no longer require the preinstall script 2016-02-09 20:18:00 +00:00
Lovell Fuller
afb30b3695 Ensure VipsArea is unreferenced after Buffer-based output
Prevents the leak of a ~1KB GMutex per output image
2016-02-09 19:28:17 +00:00
Lovell Fuller
09b019ed13 Expand glibc check to include 'gnu libc' identifier 2016-02-08 20:45:24 +00:00
Lovell Fuller
d46ac3a478 Check for glibc before downloading pre-compiled binaries #354 2016-02-08 20:06:01 +00:00
Lovell Fuller
677b2b9089 Selected output format > unknown file extension #344 2016-02-07 20:13:13 +00:00
Lovell Fuller
5c1067c63f Remove the no-longer-maintained gulp-sharp #353 2016-02-07 15:35:41 +00:00
Lovell Fuller
736c04a7a4 Only set density option when using magick loader
to reduce number of warnings from libvips #352
2016-02-04 19:16:54 +00:00
Lovell Fuller
0e29c55d13 Merge branch 'master' of https://github.com/lovell/sharp 2016-02-04 19:11:51 +00:00
Lovell Fuller
ca49e6079c Increase threshold for gamma=0 test, due to either
the version of libjpeg or a cumulative rounding error.
2016-02-04 19:10:58 +00:00
Lovell Fuller
320a7464c7 Merge pull request #351 from joelmukuthu/master
Fix: default crop gravity to sharp.gravity.center
2016-02-04 10:11:48 +00:00
Joel Mukuthu
da74cd078f Fix: default crop gravity to sharp.gravity.center
Closes https://github.com/lovell/sharp/issues/350
2016-02-04 10:38:58 +01:00
Lovell Fuller
322aa60891 Ensure 16-bit input images can be normalised 2016-02-03 19:33:34 +00:00
Lovell Fuller
e380576da2 Add support for raw, uncompressed pixel Buffer/Stream input 2016-02-03 19:21:37 +00:00
Lovell Fuller
cf7664a854 Improve SVG support by allowing control of density/DPI
Switch pre-built libs from Imagemagick to Graphicsmagick
2016-02-03 17:48:22 +00:00
Lovell Fuller
56508e8d79 Add support for libvips' PPM and FITS loaders #347 2016-02-03 17:48:22 +00:00
Lovell Fuller
2656c69d99 Upgrade to libvips v8.2.2 2016-02-03 17:48:22 +00:00
Lovell Fuller
57c1e3ae26 Slightly simplify marshalling of data from V8 Objects 2016-02-03 17:48:22 +00:00
Lovell Fuller
2675b2265b Ensure 16-bit input images embed onto alpha background
Support gamma correction of images with alpha channel
Favour shrink over affine when reducing by integral factor
2016-02-03 17:48:22 +00:00
Lovell Fuller
41e50770d1 Update benchmark test dependencies 2016-02-03 17:48:22 +00:00
Lovell Fuller
b3d6e94984 Optimisation for integral factors: favour shrink over affine 2016-02-03 17:48:22 +00:00
Lovell Fuller
5c9c17f1f6 Switch from libvips' C to C++ binding
Requires upgrade to libvips 8.2.1
2016-02-03 17:48:22 +00:00
Lovell Fuller
11329d5e09 Expose control of the number of open files in libvips' cache.
Breaks API of existing cache method.
Disable libvips cache for I/O tests.
2016-02-03 17:48:22 +00:00
Lovell Fuller
8843211e12 Upgrade libvips to v8.2.2 2016-01-31 11:54:19 +00:00
70 changed files with 5425 additions and 1765 deletions

View File

@@ -4,7 +4,8 @@
"maxparams": 4, "maxparams": 4,
"maxcomplexity": 13, "maxcomplexity": 13,
"globals": { "globals": {
"before": true, "beforeEach": true,
"afterEach": true,
"describe": true, "describe": true,
"it": true "it": true
} }

View File

@@ -41,8 +41,9 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch | | Release | WIP branch |
| ------: | :--------- | | ------: | :--------- |
| v0.12.0 | look | | v0.14.0 | needle |
| v0.13.0 | mind | | v0.15.0 | outfit |
| v0.16.0 | pencil |
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`. Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.

View File

@@ -1,6 +1,53 @@
{ {
'targets': [{ 'targets': [{
'target_name': 'libvips-cpp',
'conditions': [
['OS == "win"', {
# Build libvips C++ binding for Windows due to MSVC std library ABI changes
'type': 'shared_library',
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'defines': [
'VIPS_CPLUSPLUS_EXPORTS'
],
'sources': [
'src/libvips/cplusplus/VError.cpp',
'src/libvips/cplusplus/VInterpolate.cpp',
'src/libvips/cplusplus/VImage.cpp'
],
'include_dirs': [
'<(module_root_dir)/include',
'<(module_root_dir)/include/glib-2.0',
'<(module_root_dir)/lib/glib-2.0/include'
],
'libraries': [
'<(module_root_dir)/lib/libvips.lib',
'<(module_root_dir)/lib/libglib-2.0.lib',
'<(module_root_dir)/lib/libgobject-2.0.lib'
],
'configurations': {
'Release': {
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
},
'msvs_disabled_warnings': [
4275
]
}
}
}, {
# Ignore this target for non-Windows
'type': 'none'
}]
]
}, {
'target_name': 'sharp', 'target_name': 'sharp',
'dependencies': [
'libvips-cpp'
],
# Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi # Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
'variables': { 'variables': {
'variables': { 'variables': {
@@ -17,7 +64,7 @@
'conditions': [ 'conditions': [
['OS != "win"', { ['OS != "win"', {
# Which version, if any, of libvips is available globally via pkg-config? # Which version, if any, of libvips is available globally via pkg-config?
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips 2>/dev/null || true)' 'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips-cpp 2>/dev/null || true)'
}, { }, {
'global_vips_version': '' 'global_vips_version': ''
}] }]
@@ -43,18 +90,21 @@
'src/sharp.cc', 'src/sharp.cc',
'src/utilities.cc' 'src/utilities.cc'
], ],
'defines': [
'_GLIBCXX_USE_CXX11_ABI=0'
],
'include_dirs': [ 'include_dirs': [
'<!(node -e "require(\'nan\')")' '<!(node -e "require(\'nan\')")'
], ],
'conditions': [ 'conditions': [
['use_global_vips == "true"', { ['use_global_vips == "true"', {
# Use pkg-config for include and lib # Use pkg-config for include and lib
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips glib-2.0 | sed s\/-I//g)'], 'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'],
'conditions': [ 'conditions': [
['runtime_link == "static"', { ['runtime_link == "static"', {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips)'] 'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips-cpp)']
}, { }, {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips)'] 'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)']
}] }]
] ]
}, { }, {
@@ -66,9 +116,6 @@
], ],
'conditions': [ 'conditions': [
['OS == "win"', { ['OS == "win"', {
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'libraries': [ 'libraries': [
'<(module_root_dir)/lib/libvips.lib', '<(module_root_dir)/lib/libvips.lib',
'<(module_root_dir)/lib/libglib-2.0.lib', '<(module_root_dir)/lib/libglib-2.0.lib',
@@ -80,12 +127,13 @@
'download_vips': '<!(LDD_VERSION="<!(ldd --version 2>&1 || true)" node -e "require(\'./binding\').download_vips()")' 'download_vips': '<!(LDD_VERSION="<!(ldd --version 2>&1 || true)" node -e "require(\'./binding\').download_vips()")'
}, },
'libraries': [ 'libraries': [
'<(module_root_dir)/lib/libvips-cpp.so',
'<(module_root_dir)/lib/libvips.so', '<(module_root_dir)/lib/libvips.so',
'<(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/libMagickCore-6.Q16.so', '<(module_root_dir)/lib/libGraphicsMagick.so',
'<(module_root_dir)/lib/libMagickWand-6.Q16.so', '<(module_root_dir)/lib/libGraphicsMagickWand.so',
'<(module_root_dir)/lib/libexif.so', '<(module_root_dir)/lib/libexif.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',
@@ -118,6 +166,7 @@
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11', 'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
'CLANG_CXX_LIBRARY': 'libc++', 'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.7', 'MACOSX_DEPLOYMENT_TARGET': '10.7',
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'OTHER_CPLUSPLUSFLAGS': [ 'OTHER_CPLUSPLUSFLAGS': [
'-fexceptions', '-fexceptions',
'-Wall', '-Wall',
@@ -130,7 +179,10 @@
'VCCLCompilerTool': { 'VCCLCompilerTool': {
'ExceptionHandling': 1 'ExceptionHandling': 1
} }
} },
'msvs_disabled_warnings': [
4275
]
} }
}, },
}, { }, {

View File

@@ -61,12 +61,16 @@ module.exports.download_vips = function() {
if (process.arch === 'ia32') { if (process.arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/'); error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/');
} }
// Ensure libc >= 2.15 // Ensure glibc >= 2.15
var lddVersion = process.env.LDD_VERSION; var lddVersion = process.env.LDD_VERSION;
if (lddVersion) { if (lddVersion) {
var libcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : ''; if (/(glibc|gnu libc)/i.test(lddVersion)) {
if (libcVersion && semver.lt(libcVersion + '.0', '2.13.0')) { var glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
error('libc version ' + libcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/'); if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) {
error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
} else {
error(lddVersion.split(/\n/)[0] + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
} }
} }
// Arch/platform-specific .tar.gz // Arch/platform-specific .tar.gz

View File

@@ -6,23 +6,28 @@ var sharp = require('sharp');
### Input ### Input
#### sharp([input]) #### sharp([input], [options])
Constructor to which further methods are chained. `input`, if present, can be one of: Constructor to which further methods are chained.
* Buffer containing JPEG, PNG, WebP, GIF* or TIFF image data, or `input`, if present, can be one of:
* 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 image file, with most major formats supported.
The object returned implements the JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data
can be streamed into the object when `input` is `null` or `undefined`.
`options`, if present, is an Object with the following optional attributes:
* `density` an integral number representing the DPI for vector images, defaulting to 72.
* `raw` an Object containing `width`, `height` and `channels` when providing uncompressed data. See `raw()` for pixel ordering.
The object returned by the constructor implements the
[stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class. [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
JPEG, PNG, WebP, GIF* or TIFF format image data
can be streamed into the object when `input` is not provided.
JPEG, PNG or WebP format image data can be streamed out from this object. JPEG, PNG or WebP format image data can be streamed out from this object.
\* libvips 8.0.0+ is required for Buffer/Stream input of GIF and other `magick` formats.
```javascript ```javascript
sharp('input.jpg') sharp('input.jpg')
.resize(300, 200) .resize(300, 200)
@@ -38,7 +43,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` and `openslide`) * `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`)
* `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)
@@ -391,12 +396,14 @@ sharp('input.png')
#### toFile(path, [callback]) #### toFile(path, [callback])
`path` is a String containing the path to write the image data to. The format is inferred from the extension, with JPEG, PNG, WebP, TIFF and DZI supported. `path` is a String containing the path to write the image data to.
If an explicit output format is not selected, it will be inferred from the extension, with JPEG, PNG, WebP, TIFF and DZI supported.
`callback`, if present, is called with two arguments `(err, info)` where: `callback`, if present, is called with two arguments `(err, info)` where:
* `err` contains an error message, if any. * `err` contains an error message, if any.
* `info` contains the output image `format`, `size` (bytes), `width` and `height`. * `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
A Promises/A+ promise is returned when `callback` is not provided. A Promises/A+ promise is returned when `callback` is not provided.
@@ -408,7 +415,7 @@ Write image data to a Buffer, the format of which will match the input image by
* `err` is an error message, if any. * `err` is an error message, if any.
* `buffer` is the output image data. * `buffer` is the output image data.
* `info` contains the output image `format`, `size` (bytes), `width` and `height`. * `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
A Promises/A+ promise is returned when `callback` is not provided. A Promises/A+ promise is returned when `callback` is not provided.
@@ -590,19 +597,26 @@ An Object containing the version numbers of libvips and, on Linux, its dependenc
### Utilities ### Utilities
#### sharp.cache([memory], [items]) #### sharp.cache([options])
If `memory` or `items` are provided, set the limits of _libvips'_ operation cache. If `options` is provided, sets the limits of _libvips'_ operation cache.
* `memory` is the maximum memory in MB to use for this cache, with a default value of 100 * `options.memory` is the maximum memory in MB to use for this cache, with a default value of 50
* `items` is the maximum number of operations to cache, with a default value of 500 * `options.files` is the maximum number of files to hold open, with a default value of 20
* `options.items` is the maximum number of operations to cache, with a default value of 100
`options` can also be a boolean, where `true` enables the default cache settings and `false` disables all caching.
This method always returns cache statistics, useful for determining how much working memory is required for a particular task. This method always returns cache statistics, useful for determining how much working memory is required for a particular task.
```javascript ```javascript
var stats = sharp.cache(); // { current: 75, high: 99, memory: 100, items: 500 } var stats = sharp.cache();
sharp.cache(200); // { current: 75, high: 99, memory: 200, items: 500 } ```
sharp.cache(50, 200); // { current: 49, high: 99, memory: 50, items: 200}
```javascript
sharp.cache( { items: 200 } );
sharp.cache( { files: 0 } );
sharp.cache(false);
``` ```
#### sharp.concurrency([threads]) #### sharp.concurrency([threads])

View File

@@ -1,5 +1,58 @@
# Changelog # Changelog
### v0.13 - "*mind*"
#### v0.13.1 - 27<sup>th</sup> February 2016
* Fix embedding onto transparent backgrounds; regression introduced in v0.13.0.
[#366](https://github.com/lovell/sharp/issues/366)
[@diegocsandrim](https://github.com/diegocsandrim)
#### v0.13.0 - 15<sup>th</sup> February 2016
* Improve vector image support by allowing control of density/DPI.
Switch pre-built libs from Imagemagick to Graphicsmagick.
[#110](https://github.com/lovell/sharp/issues/110)
[@bradisbell](https://github.com/bradisbell)
* Add support for raw, uncompressed pixel Buffer/Stream input.
[#220](https://github.com/lovell/sharp/issues/220)
[@mikemorris](https://github.com/mikemorris)
* Switch from libvips' C to C++ bindings, requires upgrade to v8.2.2.
[#299](https://github.com/lovell/sharp/issues/299)
* Control number of open files in libvips' cache; breaks existing `cache` behaviour.
[#315](https://github.com/lovell/sharp/issues/315)
[@impomezia](https://github.com/impomezia)
* Ensure 16-bit input images can be normalised and embedded onto transparent backgrounds.
[#339](https://github.com/lovell/sharp/issues/339)
[#340](https://github.com/lovell/sharp/issues/340)
[@janaz](https://github.com/janaz)
* Ensure selected format takes precedence over any unknown output filename extension.
[#344](https://github.com/lovell/sharp/issues/344)
[@ubaltaci](https://github.com/ubaltaci)
* Add support for libvips' PBM, PGM, PPM and FITS image format loaders.
[#347](https://github.com/lovell/sharp/issues/347)
[@oaleynik](https://github.com/oaleynik)
* Ensure default crop gravity is center/centre.
[#351](https://github.com/lovell/sharp/pull/351)
[@joelmukuthu](https://github.com/joelmukuthu)
* Improve support for musl libc systems e.g. Alpine Linux.
[#354](https://github.com/lovell/sharp/issues/354)
[#359](https://github.com/lovell/sharp/pull/359)
[@download13](https://github.com/download13)
[@wjordan](https://github.com/wjordan)
* Small optimisation when reducing by an integral factor to favour shrink over affine.
* Add support for gamma correction of images with an alpha channel.
### v0.12 - "*look*" ### v0.12 - "*look*"
#### v0.12.2 - 16<sup>th</sup> January 2016 #### v0.12.2 - 16<sup>th</sup> January 2016

View File

@@ -4,8 +4,8 @@ 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 of many 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 Resizing an image is typically 4x faster than using the
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. Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not sacrificed for speed.
@@ -13,7 +13,7 @@ Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not s
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.
64-bit Windows and recent Linux systems do not require Most Windows (x64), Linux and ARMv6+ systems do not require
the installation of any external runtime dependencies. the installation of any external runtime dependencies.
Use with OS X is as simple as running `brew install homebrew/science/vips` Use with OS X is as simple as running `brew install homebrew/science/vips`
@@ -24,7 +24,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, OpenSlide,
GIF and other libmagick-supported formats. 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.

View File

@@ -17,7 +17,7 @@ npm install sharp
libvips and its dependencies are fetched and stored within `node_modules/sharp` during `npm install`. libvips and its dependencies are fetched and stored within `node_modules/sharp` during `npm install`.
This involves an automated HTTPS download of approximately 6MB. This involves an automated HTTPS download of approximately 6MB.
Most recent Linux-based operating systems 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, 14.10, 15.04, 15.10
@@ -41,6 +41,10 @@ the following command as a user with `sudo` access
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 -
``` ```
For Linux-based operating systems such as Alpine that use musl libc,
the smaller stack size means libvips' cache should be disabled
via `sharp.cache(false)` to avoid a stack overflow.
### Mac OS ### Mac OS
[![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp-osx-ci.png?branch=master)](https://travis-ci.org/lovell/sharp-osx-ci) [![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp-osx-ci.png?branch=master)](https://travis-ci.org/lovell/sharp-osx-ci)
@@ -112,5 +116,4 @@ docker pull wjordan/libvips
### Build tools ### Build tools
* [gulp-responsive](https://www.npmjs.com/package/gulp-responsive) * [gulp-responsive](https://www.npmjs.com/package/gulp-responsive)
* [gulp-sharp](https://www.npmjs.com/package/gulp-sharp)
* [grunt-sharp](https://www.npmjs.com/package/grunt-sharp) * [grunt-sharp](https://www.npmjs.com/package/grunt-sharp)

View File

@@ -4,16 +4,16 @@
* 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 2015.09.1
* Node.js v5.1.0 * Node.js v5.5.0
### The contenders ### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.2.19 - Image processing in pure JavaScript. Bilinear interpolation only. * [jimp](https://www.npmjs.com/package/jimp) v0.2.20 - 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.8 - Wrapper around CImg, compiles dependencies from source.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) @5ab570e - Wrapper around libmagick++, supports Buffers only. * [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) @47c7329 - 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.21.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.12.0 / libvips v8.1.1 - Caching within libvips disabled to ensure a fair comparison. * sharp v0.13.0 / libvips v8.2.2 - Caching within libvips disabled to ensure a fair comparison.
### The task ### The task
@@ -23,19 +23,19 @@ Decompress a 2725x2225 JPEG image, resize to 720x480 using bicubic interpolation
| Module | Input | Output | Ops/sec | Speed-up | | Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: | | :----------------- | :----- | :----- | ------: | -------: |
| jimp | file | file | 0.99 | 1.0 | | jimp (bilinear) | file | file | 1.04 | 1.0 |
| jimp | buffer | buffer | 1.05 | 1.1 | | jimp (bilinear) | buffer | buffer | 1.07 | 1.0 |
| lwip | file | file | 1.13 | 1.1 | | lwip | file | file | 1.13 | 1.1 |
| lwip | buffer | buffer | 1.13 | 1.1 | | lwip | buffer | buffer | 1.13 | 1.1 |
| imagemagick-native | buffer | buffer | 1.67 | 1.7 | | imagemagick-native | buffer | buffer | 1.65 | 1.6 |
| imagemagick | file | file | 5.19 | 5.2 | | imagemagick | file | file | 5.02 | 4.8 |
| gm | buffer | buffer | 5.56 | 5.6 | | gm | buffer | buffer | 5.36 | 5.2 |
| gm | file | file | 5.59 | 5.6 | | gm | file | file | 5.39 | 5.2 |
| sharp | stream | stream | 21.91 | 22.1 | | sharp | stream | stream | 22.00 | 21.2 |
| sharp | file | file | 22.79 | 23.0 | | sharp | file | file | 22.87 | 22.0 |
| sharp | file | buffer | 22.91 | 23.1 | | sharp | file | buffer | 23.03 | 22.1 |
| sharp | buffer | file | 23.03 | 23.3 | | sharp | buffer | file | 23.10 | 22.2 |
| sharp | buffer | buffer | 23.15 | 23.4 | | sharp | buffer | buffer | 23.21 | 22.3 |
Greater performance can be expected with caching enabled (default) and using 8+ core machines. Greater performance can be expected with caching enabled (default) and using 8+ core machines.

126
index.js
View File

@@ -35,9 +35,9 @@ var maximum = {
}; };
// Constructor-factory // Constructor-factory
var Sharp = function(input) { var Sharp = function(input, options) {
if (!(this instanceof Sharp)) { if (!(this instanceof Sharp)) {
return new Sharp(input); return new Sharp(input, options);
} }
stream.Duplex.call(this); stream.Duplex.call(this);
this.options = { this.options = {
@@ -46,6 +46,10 @@ var Sharp = function(input) {
streamIn: false, streamIn: false,
sequentialRead: false, sequentialRead: false,
limitInputPixels: maximum.pixels, limitInputPixels: maximum.pixels,
density: '72',
rawWidth: 0,
rawHeight: 0,
rawChannels: 0,
// ICC profiles // ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep, iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options // resize options
@@ -82,7 +86,8 @@ var Sharp = function(input) {
// overlay // overlay
overlayPath: '', overlayPath: '',
// output options // output options
output: '__input', formatOut: 'input',
fileOut: '',
progressive: false, progressive: false,
quality: 80, quality: 80,
compressionLevel: 6, compressionLevel: 6,
@@ -107,12 +112,13 @@ var Sharp = function(input) {
} else if (typeof input === 'object' && input instanceof Buffer) { } else if (typeof input === 'object' && input instanceof Buffer) {
// input=buffer // input=buffer
this.options.bufferIn = input; this.options.bufferIn = input;
} else if (typeof input === 'undefined') { } else if (typeof input === 'undefined' || input === null) {
// input=stream // input=stream
this.options.streamIn = true; this.options.streamIn = true;
} else { } else {
throw new Error('Unsupported input ' + typeof input); throw new Error('Unsupported input ' + typeof input);
} }
this._inputOptions(options);
return this; return this;
}; };
module.exports = Sharp; module.exports = Sharp;
@@ -133,6 +139,56 @@ module.exports.format = sharp.format();
*/ */
module.exports.versions = versions; module.exports.versions = versions;
/*
Validation helpers
*/
var isDefined = function(val) {
return typeof val !== 'undefined' && val !== null;
};
var isObject = function(val) {
return typeof val === 'object';
};
var isInteger = function(val) {
return typeof val === 'number' && !Number.isNaN(val) && val % 1 === 0;
};
var inRange = function(val, min, max) {
return val >= min && val <= max;
};
/*
Set input-related options
density: DPI at which to load vector images via libmagick
*/
Sharp.prototype._inputOptions = function(options) {
if (isObject(options)) {
// Density
if (isDefined(options.density)) {
if (isInteger(options.density) && inRange(options.density, 1, 2400)) {
this.options.density = options.density.toString();
} else {
throw new Error('Invalid density (1 to 2400) ' + options.density);
}
}
// Raw pixel input
if (isDefined(options.raw)) {
if (
isObject(options.raw) &&
isInteger(options.raw.width) && inRange(options.raw.width, 1, maximum.width) &&
isInteger(options.raw.height) && inRange(options.raw.height, 1, maximum.height) &&
isInteger(options.raw.channels) && inRange(options.raw.channels, 1, 4)
) {
this.options.rawWidth = options.raw.width;
this.options.rawHeight = options.raw.height;
this.options.rawChannels = options.raw.channels;
} else {
throw new Error('Expected width, height and channels for raw pixel input');
}
}
} else if (isDefined(options)) {
throw new Error('Invalid input options ' + options);
}
};
/* /*
Handle incoming chunk on Writable Stream Handle incoming chunk on Writable Stream
*/ */
@@ -176,7 +232,9 @@ module.exports.gravity = {
Sharp.prototype.crop = function(gravity) { Sharp.prototype.crop = function(gravity) {
this.options.canvas = 'crop'; this.options.canvas = 'crop';
if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 8) { if (typeof gravity === 'undefined') {
this.options.gravity = module.exports.gravity.center;
} else if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 8) {
this.options.gravity = gravity; this.options.gravity = gravity;
} else if (typeof gravity === 'string' && typeof module.exports.gravity[gravity] === 'number') { } else if (typeof gravity === 'string' && typeof module.exports.gravity[gravity] === 'number') {
this.options.gravity = module.exports.gravity[gravity]; this.options.gravity = module.exports.gravity[gravity];
@@ -610,8 +668,8 @@ Sharp.prototype.limitInputPixels = function(limit) {
/* /*
Write output image data to a file Write output image data to a file
*/ */
Sharp.prototype.toFile = function(output, callback) { Sharp.prototype.toFile = function(fileOut, callback) {
if (!output || output.length === 0) { if (!fileOut || fileOut.length === 0) {
var errOutputInvalid = new Error('Invalid output'); var errOutputInvalid = new Error('Invalid output');
if (typeof callback === 'function') { if (typeof callback === 'function') {
callback(errOutputInvalid); callback(errOutputInvalid);
@@ -619,7 +677,7 @@ Sharp.prototype.toFile = function(output, callback) {
return BluebirdPromise.reject(errOutputInvalid); return BluebirdPromise.reject(errOutputInvalid);
} }
} else { } else {
if (this.options.fileIn === output) { if (this.options.fileIn === fileOut) {
var errOutputIsInput = new Error('Cannot use same file for input and output'); var errOutputIsInput = new Error('Cannot use same file for input and output');
if (typeof callback === 'function') { if (typeof callback === 'function') {
callback(errOutputIsInput); callback(errOutputIsInput);
@@ -627,7 +685,7 @@ Sharp.prototype.toFile = function(output, callback) {
return BluebirdPromise.reject(errOutputIsInput); return BluebirdPromise.reject(errOutputIsInput);
} }
} else { } else {
this.options.output = output; this.options.fileOut = fileOut;
return this._pipeline(callback); return this._pipeline(callback);
} }
} }
@@ -645,7 +703,7 @@ Sharp.prototype.toBuffer = function(callback) {
Force JPEG output Force JPEG output
*/ */
Sharp.prototype.jpeg = function() { Sharp.prototype.jpeg = function() {
this.options.output = '__jpeg'; this.options.formatOut = 'jpeg';
return this; return this;
}; };
@@ -653,7 +711,7 @@ Sharp.prototype.jpeg = function() {
Force PNG output Force PNG output
*/ */
Sharp.prototype.png = function() { Sharp.prototype.png = function() {
this.options.output = '__png'; this.options.formatOut = 'png';
return this; return this;
}; };
@@ -661,7 +719,7 @@ Sharp.prototype.png = function() {
Force WebP output Force WebP output
*/ */
Sharp.prototype.webp = function() { Sharp.prototype.webp = function() {
this.options.output = '__webp'; this.options.formatOut = 'webp';
return this; return this;
}; };
@@ -669,7 +727,7 @@ Sharp.prototype.webp = function() {
Force raw, uint8 output Force raw, uint8 output
*/ */
Sharp.prototype.raw = function() { Sharp.prototype.raw = function() {
this.options.output = '__raw'; this.options.formatOut = 'raw';
return this; return this;
}; };
@@ -677,15 +735,17 @@ Sharp.prototype.raw = function() {
Force output to a given format Force output to a given format
@param format is either the id as a String or an Object with an 'id' attribute @param format is either the id as a String or an Object with an 'id' attribute
*/ */
Sharp.prototype.toFormat = function(format) { Sharp.prototype.toFormat = function(formatOut) {
var id = format; if (isObject(formatOut) && isDefined(formatOut.id)) {
if (typeof format === 'object') { formatOut = formatOut.id;
id = format.id;
} }
if (typeof id === 'string' && typeof module.exports.format[id] === 'object' && typeof this[id] === 'function') { if (
this[id](); isDefined(formatOut) &&
['jpeg', 'png', 'webp', 'raw', 'tiff', 'dz', 'input'].indexOf(formatOut) !== -1
) {
this.options.formatOut = formatOut;
} else { } else {
throw new Error('Unsupported format ' + format); throw new Error('Unsupported output format ' + formatOut);
} }
return this; return this;
}; };
@@ -824,7 +884,6 @@ Sharp.prototype.clone = function() {
// Clone existing options // Clone existing options
var clone = new Sharp(); var clone = new Sharp();
util._extend(clone.options, this.options); util._extend(clone.options, this.options);
clone.streamIn = false;
// Pass 'finish' event to clone for Stream-based input // Pass 'finish' event to clone for Stream-based input
this.on('finish', function() { this.on('finish', function() {
// Clone inherits input data // Clone inherits input data
@@ -834,18 +893,25 @@ Sharp.prototype.clone = function() {
return clone; return clone;
}; };
/* /**
Get and set cache memory and item limits Get and set cache memory, file and item limits
*/ */
module.exports.cache = function(memory, items) { module.exports.cache = function(options) {
if (typeof memory !== 'number' || Number.isNaN(memory)) { if (typeof options === 'boolean') {
memory = null; if (options) {
// Default cache settings of 50MB, 20 files, 100 items
return sharp.cache(50, 20, 100);
} else {
return sharp.cache(0, 0, 0);
}
} else if (typeof options === 'object') {
return sharp.cache(options.memory, options.files, options.items);
} else {
return sharp.cache();
} }
if (typeof items !== 'number' || Number.isNaN(items)) {
items = null;
}
return sharp.cache(memory, items);
}; };
// Ensure default cache settings are set
module.exports.cache(true);
/* /*
Get and set size of thread pool Get and set size of thread pool

View File

@@ -1,6 +1,6 @@
{ {
"name": "sharp", "name": "sharp",
"version": "0.12.2", "version": "0.13.1",
"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>",
@@ -47,28 +47,28 @@
"vips" "vips"
], ],
"dependencies": { "dependencies": {
"bluebird": "^3.1.1", "bluebird": "^3.3.3",
"color": "^0.11.1", "color": "^0.11.1",
"nan": "^2.2.0", "nan": "^2.2.0",
"semver": "^5.1.0", "semver": "^5.1.0",
"request": "^2.67.0", "request": "^2.69.0",
"tar": "^2.2.1" "tar": "^2.2.1"
}, },
"devDependencies": { "devDependencies": {
"async": "^1.5.2", "async": "^1.5.2",
"coveralls": "^2.11.6", "coveralls": "^2.11.8",
"exif-reader": "^1.0.0", "exif-reader": "^1.0.0",
"icc": "^0.0.2", "icc": "^0.0.2",
"istanbul": "^0.4.2", "istanbul": "^0.4.2",
"mocha": "^2.3.4", "mocha": "^2.4.5",
"mocha-jshint": "^2.2.6", "mocha-jshint": "^2.3.1",
"node-cpplint": "^0.4.0", "node-cpplint": "^0.4.0",
"rimraf": "^2.5.0", "rimraf": "^2.5.2",
"bufferutil": "^1.2.1" "bufferutil": "^1.2.1"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"config": { "config": {
"libvips": "8.2.0" "libvips": "8.2.2"
}, },
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"

View File

@@ -13,22 +13,24 @@ export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig"
export PATH="${PATH}:${TARGET}/bin" export PATH="${PATH}:${TARGET}/bin"
export CPPFLAGS="-I${TARGET}/include" export CPPFLAGS="-I${TARGET}/include"
export LDFLAGS="-L${TARGET}/lib" export LDFLAGS="-L${TARGET}/lib"
export CFLAGS="-O3"
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.46.2 VERSION_GLIB=2.47.5
VERSION_XML2=2.9.3 VERSION_XML2=2.9.3
VERSION_GSF=1.14.34 VERSION_GSF=1.14.34
VERSION_EXIF=0.6.21 VERSION_EXIF=0.6.21
VERSION_JPEG=1.4.2
VERSION_PNG16=1.6.20
VERSION_LCMS2=2.7 VERSION_LCMS2=2.7
VERSION_GM=1.3.23
VERSION_JPEG=1.4.2
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_MAGICK=6.9.2-10
VERSION_ORC=0.4.24 VERSION_ORC=0.4.24
VERSION_VIPS=8.2.0 VERSION_VIPS=8.2.2
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
@@ -42,9 +44,9 @@ 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 http://ftp.gnome.org/pub/gnome/sources/glib/2.46/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 curl -Ls https://download.gnome.org/sources/glib/2.47/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 && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip
mkdir ${DEPS}/xml2 mkdir ${DEPS}/xml2
curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1 curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1
@@ -52,7 +54,7 @@ cd ${DEPS}/xml2
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip
mkdir ${DEPS}/gsf mkdir ${DEPS}/gsf
curl -Ls http://ftp.gnome.org/pub/GNOME/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1 curl -Ls https://download.gnome.org/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1
cd ${DEPS}/gsf 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
@@ -61,6 +63,16 @@ curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF
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
curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
cd ${DEPS}/lcms2
./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 http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
cd ${DEPS}/jpeg cd ${DEPS}/jpeg
@@ -71,27 +83,17 @@ curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG1
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
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
cd ${DEPS}/lcms2
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/webp mkdir ${DEPS}/webp
curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1 curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
cd ${DEPS}/webp cd ${DEPS}/webp
./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}/tiff mkdir ${DEPS}/tiff
curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz /deps/tiff.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1
cd ${DEPS}/tiff cd ${DEPS}/tiff
./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
rm ${TARGET}/lib/libtiffxx* rm ${TARGET}/lib/libtiffxx*
mkdir ${DEPS}/magick
curl -Ls http://www.imagemagick.org/download/releases/ImageMagick-${VERSION_MAGICK}.tar.xz | tar xJC ${DEPS}/magick --strip-components=1
cd ${DEPS}/magick
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip
mkdir ${DEPS}/orc mkdir ${DEPS}/orc
curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1 curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1
cd ${DEPS}/orc cd ${DEPS}/orc
@@ -101,34 +103,34 @@ 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.2/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 \ --disable-debug --disable-introspection --without-python --without-fftw --with-magickpackage=GraphicsMagick \
--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
# Remove the C++ bindings # Remove the old C++ bindings
cd ${TARGET}/include cd ${TARGET}/include
rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h rm -rf vips/vipsc++.h vips/vipscpp.h
cd ${TARGET}/lib cd ${TARGET}/lib
rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.* 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\
\"zlib\": \"${VERSION_ZLIB}\",\n\ \"exif\": \"${VERSION_EXIF}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\ \"ffi\": \"${VERSION_FFI}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\ \"glib\": \"${VERSION_GLIB}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\ \"gsf\": \"${VERSION_GSF}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\ \"jpeg\": \"${VERSION_JPEG}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\ \"lcms\": \"${VERSION_LCMS2}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\ \"gm\": \"${VERSION_GM}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"magick\": \"${VERSION_MAGICK}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\ \"orc\": \"${VERSION_ORC}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\ \"vips\": \"${VERSION_VIPS}\"\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\
}" >lib/versions.json }" >lib/versions.json
# Create .tar.gz # Create .tar.gz

View File

@@ -13,14 +13,14 @@ fi
docker build -t vips-dev-win win docker build -t vips-dev-win win
WIN_CONTAINER_ID=$(docker run -d vips-dev-win) WIN_CONTAINER_ID=$(docker run -d vips-dev-win)
docker cp $WIN_CONTAINER_ID:/libvips-8.2.0-win.tar.gz . docker cp $WIN_CONTAINER_ID:/libvips-8.2.2-win.tar.gz .
docker rm $WIN_CONTAINER_ID docker rm $WIN_CONTAINER_ID
# Linux # Linux
docker build -t vips-dev-lin lin docker build -t vips-dev-lin lin
LIN_CONTAINER_ID=$(docker run -d vips-dev-lin) LIN_CONTAINER_ID=$(docker run -d vips-dev-lin)
docker cp $LIN_CONTAINER_ID:/libvips-8.2.0-lin.tar.gz . docker cp $LIN_CONTAINER_ID:/libvips-8.2.2-lin.tar.gz .
docker rm $LIN_CONTAINER_ID docker rm $LIN_CONTAINER_ID
# Checksums # Checksums

View File

@@ -13,23 +13,25 @@ RUN mkdir ${DEPS} && mkdir ${TARGET}
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" \
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.46.2 \ VERSION_GLIB=2.47.5 \
VERSION_XML2=2.9.3 \ VERSION_XML2=2.9.3 \
VERSION_GSF=1.14.34 \ VERSION_GSF=1.14.34 \
VERSION_EXIF=0.6.21 \ VERSION_EXIF=0.6.21 \
VERSION_JPEG=1.4.2 \
VERSION_PNG16=1.6.20 \
VERSION_LCMS2=2.7 \ VERSION_LCMS2=2.7 \
VERSION_GM=1.3.23 \
VERSION_JPEG=1.4.2 \
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_MAGICK=6.9.2-10 \
VERSION_ORC=0.4.24 \ VERSION_ORC=0.4.24 \
VERSION_VIPS=8.2.0 VERSION_VIPS=8.2.2
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
@@ -43,9 +45,9 @@ 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 http://ftp.gnome.org/pub/gnome/sources/glib/2.46/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 RUN curl -Ls https://download.gnome.org/sources/glib/2.47/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 && make install-strip RUN ./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
@@ -53,7 +55,7 @@ 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 --with-zlib=${TARGET} && make install-strip
RUN mkdir ${DEPS}/gsf RUN mkdir ${DEPS}/gsf
RUN curl -Ls http://ftp.gnome.org/pub/GNOME/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
WORKDIR ${DEPS}/gsf 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
@@ -62,6 +64,16 @@ RUN curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_
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 curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
WORKDIR ${DEPS}/lcms2
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 http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
WORKDIR ${DEPS}/jpeg WORKDIR ${DEPS}/jpeg
@@ -72,27 +84,17 @@ RUN curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_
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
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
WORKDIR ${DEPS}/lcms2
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/webp RUN mkdir ${DEPS}/webp
RUN curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1 RUN curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
WORKDIR ${DEPS}/webp WORKDIR ${DEPS}/webp
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}/tiff RUN mkdir ${DEPS}/tiff
RUN curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz /deps/tiff.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 RUN curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1
WORKDIR ${DEPS}/tiff WORKDIR ${DEPS}/tiff
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/libtiffxx* RUN rm ${TARGET}/lib/libtiffxx*
RUN mkdir ${DEPS}/magick
RUN curl -Ls http://www.imagemagick.org/download/releases/ImageMagick-${VERSION_MAGICK}.tar.xz | tar xJC ${DEPS}/magick --strip-components=1
WORKDIR ${DEPS}/magick
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip
RUN mkdir ${DEPS}/orc 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
@@ -102,34 +104,34 @@ 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.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
WORKDIR ${DEPS}/vips WORKDIR ${DEPS}/vips
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 \ --disable-debug --disable-introspection --without-python --without-fftw --with-magickpackage=GraphicsMagick \
--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
# Remove the C++ bindings # Remove the old C++ bindings
WORKDIR ${TARGET}/include WORKDIR ${TARGET}/include
RUN rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h RUN rm -rf vips/vipsc++.h vips/vipscpp.h
WORKDIR ${TARGET}/lib WORKDIR ${TARGET}/lib
RUN rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.* 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\
\"zlib\": \"${VERSION_ZLIB}\",\n\ \"exif\": \"${VERSION_EXIF}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\ \"ffi\": \"${VERSION_FFI}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\ \"glib\": \"${VERSION_GLIB}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\ \"gsf\": \"${VERSION_GSF}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\ \"jpeg\": \"${VERSION_JPEG}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\ \"lcms\": \"${VERSION_LCMS2}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\ \"gm\": \"${VERSION_GM}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"magick\": \"${VERSION_MAGICK}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\ \"orc\": \"${VERSION_ORC}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\ \"vips\": \"${VERSION_VIPS}\"\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\
}" >lib/versions.json }" >lib/versions.json
# Create .tar.gz # Create .tar.gz

View File

@@ -6,13 +6,13 @@ if ! type docker >/dev/null; then
exit 1 exit 1
fi fi
test="npm run clean; NODE_ENV=development 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 12.04, 14.04
for dist in wheezy jessie precise trusty; do for dist in wheezy jessie precise trusty; do
echo "Testing $dist..." echo "Testing $dist..."
if docker run -i -t --rm -v $PWD:/v 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:0.12 >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
@@ -20,7 +20,7 @@ done
# Centos 6 # Centos 6
echo "Testing centos6..." echo "Testing centos6..."
if docker run -i -t --rm -v $PWD:/v nodesource/centos6:0.12 >packaging/centos6.log 2>&1 sh -c "cd /v; source ./packaging/test/centos6.sh; ./preinstall.sh; $test"; 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" then echo "centos6 OK"
else echo "centos6 fail" && cat packaging/centos6.log else echo "centos6 fail" && cat packaging/centos6.log
fi fi
@@ -29,7 +29,7 @@ fi
# Fedora 20, 21 # Fedora 20, 21
for dist in centos7 fedora20 fedora21; do for dist in centos7 fedora20 fedora21; do
echo "Testing $dist..." echo "Testing $dist..."
if docker run -i -t --rm -v $PWD:/v nodesource/$dist:0.12 >packaging/$dist.log 2>&1 sh -c "cd /v; $test"; 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";
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
@@ -48,3 +48,10 @@ if docker run -i -t --rm -v $PWD:/v base/archlinux:2015.06.01 >packaging/archlin
then echo "archlinux OK" then echo "archlinux OK"
else echo "archlinux fail" && cat packaging/archlinux.log else echo "archlinux fail" && cat packaging/archlinux.log
fi fi
# Alpine
echo "Testing alpine..."
if docker run -i -t --rm -v $PWD:/v -e "SHARP_TEST_WITHOUT_CACHE=0" alpine:edge >packaging/alpine.log 2>&1 sh -c "cd /v; ./packaging/test/alpine.sh; $test";
then echo "alpine OK"
else echo "alpine fail" && cat packaging/alpine.log
fi

7
packaging/test/alpine.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
# Install build dependencies
apk add --update make gcc g++ python nodejs
# Install libvips with build headers and dependencies
apk add libvips-dev --update --repository https://s3.amazonaws.com/wjordan-apk --allow-untrusted

View File

@@ -3,5 +3,5 @@
# Install Node.js on openSUSE 13.2 # Install Node.js on openSUSE 13.2
zypper addrepo http://download.opensuse.org/repositories/devel:languages:nodejs/openSUSE_13.2/devel:languages:nodejs.repo zypper addrepo http://download.opensuse.org/repositories/devel:languages:nodejs/openSUSE_13.2/devel:languages:nodejs.repo
zypper --gpg-auto-import-keys refresh zypper --gpg-auto-import-keys refresh
zypper --non-interactive install gcc-c++ make nodejs-devel nodejs-npm zypper --non-interactive install gcc-c++ make nodejs-devel npm
npm install -g npm npm install -g npm

View File

@@ -6,12 +6,13 @@ RUN apt-get update && apt-get install -y curl zip
# 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 -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.2.zip
RUN unzip vips-dev-w64-8.2.zip RUN unzip vips-dev-w64-8.2.2.zip
# Clean and zip # Clean and zip
WORKDIR /vips/vips-dev-8.2 WORKDIR /vips/vips-dev-8.2
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 bin/libstdc++-6.dll
RUN cp bin/*.dll lib/ RUN cp bin/*.dll lib/
RUN cp -r lib64/* lib/ RUN cp -r lib64/* lib/
RUN GZIP=-9 tar czf /libvips-8.2.0-win.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll
RUN GZIP=-9 tar czf /libvips-8.2.2-win.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll

View File

@@ -1,7 +1,14 @@
#!/bin/sh #!/bin/sh
# Ensures libvips is installed and attempts to install it if not # This script is no longer required on most
# Currently supports: # 64-bit Linux systems when using sharp v0.12.0+
# See http://sharp.dimens.io/page/install#linux
# If you really need this script, it will attempt to
# globally install libvips if not already available.
# Supports:
# * Debian Linux # * Debian Linux
# * Debian 7, 8 # * Debian 7, 8
# * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10 # * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
@@ -13,9 +20,9 @@
# * Amazon Linux 2015.03, 2015.09 # * Amazon Linux 2015.03, 2015.09
# * OpenSuse 13 # * OpenSuse 13
vips_version_minimum=8.2.0 vips_version_minimum=8.2.2
vips_version_latest_major_minor=8.2 vips_version_latest_major_minor=8.2
vips_version_latest_patch=1 vips_version_latest_patch=2
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
@@ -119,6 +126,11 @@ if [ "$(id -u)" -ne "0" ]; then
exit 1 exit 1
fi fi
# Deprecation warning
if [ "$(arch)" == "x86_64" ]; then
echo "This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
fi
# OS-specific installations of libopenslide follows # OS-specific installations of libopenslide follows
# Either openslide does not exist, or vips is installed without openslide support # Either openslide does not exist, or vips is installed without openslide support
if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_exists -eq 0 ]; then if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_exists -eq 0 ]; then

View File

@@ -1,14 +1,14 @@
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <vips/vips.h> #include <vips/vips8>
#include "common.h" #include "common.h"
// Verify platform and compiler compatibility // Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 1 && VIPS_PATCH_VERSION < 1)) #if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 2))
#error libvips version 8.1.1+ required - see http://sharp.dimens.io/page/install #error libvips version 8.2.0+ required - see http://sharp.dimens.io/page/install
#endif #endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
@@ -23,6 +23,8 @@
#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation" #define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
using vips::VImage;
namespace sharp { namespace sharp {
// How many tasks are in the queue? // How many tasks are in the queue?
@@ -51,6 +53,26 @@ namespace sharp {
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI"); return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
} }
/*
Provide a string identifier for the given image type.
*/
std::string ImageTypeId(ImageType const imageType) {
std::string id;
switch (imageType) {
case ImageType::JPEG: id = "jpeg"; break;
case ImageType::PNG: id = "png"; break;
case ImageType::WEBP: id = "webp"; break;
case ImageType::TIFF: id = "tiff"; break;
case ImageType::MAGICK: id = "magick"; break;
case ImageType::OPENSLIDE: id = "openslide"; break;
case ImageType::PPM: id = "ppm"; break;
case ImageType::FITS: id = "fits"; break;
case ImageType::RAW: id = "raw"; break;
case ImageType::UNKNOWN: id = "unknown"; break;
}
return id;
}
/* /*
Determine image format of a buffer. Determine image format of a buffer.
*/ */
@@ -58,7 +80,7 @@ namespace sharp {
ImageType imageType = ImageType::UNKNOWN; ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load_buffer(buffer, length); char const *load = vips_foreign_find_load_buffer(buffer, length);
if (load != NULL) { if (load != NULL) {
std::string loader = load; std::string const loader = load;
if (EndsWith(loader, "JpegBuffer")) { if (EndsWith(loader, "JpegBuffer")) {
imageType = ImageType::JPEG; imageType = ImageType::JPEG;
} else if (EndsWith(loader, "PngBuffer")) { } else if (EndsWith(loader, "PngBuffer")) {
@@ -74,13 +96,6 @@ namespace sharp {
return imageType; return imageType;
} }
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access) {
return vips_image_new_from_buffer(buffer, length, nullptr, "access", access, nullptr);
}
/* /*
Determine image format, reads the first few bytes of the file Determine image format, reads the first few bytes of the file
*/ */
@@ -88,7 +103,7 @@ namespace sharp {
ImageType imageType = ImageType::UNKNOWN; ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load(file); char const *load = vips_foreign_find_load(file);
if (load != nullptr) { if (load != nullptr) {
std::string loader = load; std::string const loader = load;
if (EndsWith(loader, "JpegFile")) { if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG; imageType = ImageType::JPEG;
} else if (EndsWith(loader, "Png")) { } else if (EndsWith(loader, "Png")) {
@@ -99,6 +114,10 @@ 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, "Ppm")) {
imageType = ImageType::PPM;
} else if (EndsWith(loader, "Fits")) {
imageType = ImageType::FITS;
} else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) { } else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) {
imageType = ImageType::MAGICK; imageType = ImageType::MAGICK;
} }
@@ -106,43 +125,37 @@ namespace sharp {
return imageType; return imageType;
} }
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(char const *file, VipsAccess const access) {
return vips_image_new_from_file(file, "access", access, nullptr);
}
/* /*
Does this image have an embedded profile? Does this image have an embedded profile?
*/ */
bool HasProfile(VipsImage *image) { bool HasProfile(VImage image) {
return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE; return (image.get_typeof(VIPS_META_ICC_NAME) != 0) ? TRUE : FALSE;
} }
/* /*
Does this image have an alpha channel? Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this. Uses colour space interpretation with number of channels to guess this.
*/ */
bool HasAlpha(VipsImage *image) { bool HasAlpha(VImage image) {
int const bands = image.bands();
VipsInterpretation const interpretation = image.interpretation();
return ( return (
(image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) || (bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) || (bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK) (bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK)
); );
} }
/* /*
Get EXIF Orientation of image, if any. Get EXIF Orientation of image, if any.
*/ */
int ExifOrientation(VipsImage const *image) { int ExifOrientation(VImage image) {
int orientation = 0; int orientation = 0;
const char *exif; if (image.get_typeof(EXIF_IFD0_ORIENTATION) != 0) {
if ( char const *exif = image.get_string(EXIF_IFD0_ORIENTATION);
vips_image_get_typeof(image, EXIF_IFD0_ORIENTATION) != 0 && if (exif != nullptr) {
!vips_image_get_string(image, EXIF_IFD0_ORIENTATION, &exif) orientation = atoi(&exif[0]);
) { }
orientation = atoi(&exif[0]);
} }
return orientation; return orientation;
} }
@@ -150,33 +163,19 @@ namespace sharp {
/* /*
Set EXIF Orientation of image. Set EXIF Orientation of image.
*/ */
void SetExifOrientation(VipsImage *image, int const orientation) { void SetExifOrientation(VImage image, int const orientation) {
char exif[3]; char exif[3];
g_snprintf(exif, sizeof(exif), "%d", orientation); g_snprintf(exif, sizeof(exif), "%d", orientation);
vips_image_set_string(image, EXIF_IFD0_ORIENTATION, exif); image.set(EXIF_IFD0_ORIENTATION, exif);
} }
/* /*
Remove EXIF Orientation from image. Remove EXIF Orientation from image.
*/ */
void RemoveExifOrientation(VipsImage *image) { void RemoveExifOrientation(VImage image) {
SetExifOrientation(image, 0); SetExifOrientation(image, 0);
} }
/*
Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation.
*/
int InterpolatorWindowSize(char const *name) {
VipsInterpolate *interpolator = vips_interpolate_new(name);
if (interpolator == nullptr) {
return -1;
}
int window_size = vips_interpolate_get_window_size(interpolator);
g_object_unref(interpolator);
return window_size;
}
/* /*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/ */

View File

@@ -2,6 +2,9 @@
#define SRC_COMMON_H_ #define SRC_COMMON_H_
#include <string> #include <string>
#include <vips/vips8>
using vips::VImage;
namespace sharp { namespace sharp {
@@ -12,7 +15,10 @@ namespace sharp {
WEBP, WEBP,
TIFF, TIFF,
MAGICK, MAGICK,
OPENSLIDE OPENSLIDE,
PPM,
FITS,
RAW
}; };
// How many tasks are in the queue? // How many tasks are in the queue?
@@ -28,6 +34,11 @@ namespace sharp {
bool IsTiff(std::string const &str); bool IsTiff(std::string const &str);
bool IsDz(std::string const &str); bool IsDz(std::string const &str);
/*
Provide a string identifier for the given image type.
*/
std::string ImageTypeId(ImageType const imageType);
/* /*
Determine image format of a buffer. Determine image format of a buffer.
*/ */
@@ -38,47 +49,31 @@ namespace sharp {
*/ */
ImageType DetermineImageType(char const *file); ImageType DetermineImageType(char const *file);
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access);
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(char const *file, VipsAccess const access);
/* /*
Does this image have an embedded profile? Does this image have an embedded profile?
*/ */
bool HasProfile(VipsImage *image); bool HasProfile(VImage image);
/* /*
Does this image have an alpha channel? Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this. Uses colour space interpretation with number of channels to guess this.
*/ */
bool HasAlpha(VipsImage *image); bool HasAlpha(VImage image);
/* /*
Get EXIF Orientation of image, if any. Get EXIF Orientation of image, if any.
*/ */
int ExifOrientation(VipsImage const *image); int ExifOrientation(VImage image);
/* /*
Set EXIF Orientation of image. Set EXIF Orientation of image.
*/ */
void SetExifOrientation(VipsImage *image, int const orientation); void SetExifOrientation(VImage image, int const orientation);
/* /*
Remove EXIF Orientation from image. Remove EXIF Orientation from image.
*/ */
void RemoveExifOrientation(VipsImage *image); void RemoveExifOrientation(VImage image);
/*
Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation.
*/
int InterpolatorWindowSize(char const *name);
/* /*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows

View File

@@ -0,0 +1,52 @@
// Code for error type
/*
Copyright (C) 1991-2001 The National Gallery
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <iostream>
#include <vips/vips8>
VIPS_NAMESPACE_START
std::ostream &operator<<( std::ostream &file, const VError &err )
{
err.ostream_print( file );
return( file );
}
void VError::ostream_print( std::ostream &file ) const
{
file << _what;
}
VIPS_NAMESPACE_END

View File

@@ -0,0 +1,714 @@
/* Object part of VImage class
*
* 30/12/14
* - allow set enum value from string
*/
/*
Copyright (C) 1991-2001 The National Gallery
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <vips/vips8>
#include <vips/debug.h>
/*
#define VIPS_DEBUG
#define VIPS_DEBUG_VERBOSE
*/
VIPS_NAMESPACE_START
std::vector<double>
to_vectorv( int n, ... )
{
std::vector<double> vector( n );
va_list ap;
va_start( ap, n );
for( int i = 0; i < n; i++ )
vector[i] = va_arg( ap, double );
va_end( ap );
return( vector );
}
std::vector<double>
to_vector( double value )
{
return( to_vectorv( 1, value ) );
}
std::vector<double>
to_vector( int n, double array[] )
{
std::vector<double> vector( n );
for( int i = 0; i < n; i++ )
vector[i] = array[i];
return( vector );
}
std::vector<double>
negate( std::vector<double> vector )
{
std::vector<double> new_vector( vector.size() );
for( unsigned int i = 0; i < vector.size(); i++ )
new_vector[i] = vector[i] * -1;
return( new_vector );
}
std::vector<double>
invert( std::vector<double> vector )
{
std::vector<double> new_vector( vector.size() );
for( unsigned int i = 0; i < vector.size(); i++ )
new_vector[i] = 1.0 / vector[i];
return( new_vector );
}
VOption::~VOption()
{
std::list<Pair *>::iterator i;
for( i = options.begin(); i != options.end(); ++i )
delete *i;
}
// input bool
VOption *
VOption::set( const char *name, bool value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, G_TYPE_BOOLEAN );
g_value_set_boolean( &pair->value, value );
options.push_back( pair );
return( this );
}
// input int ... this path is used for enums as well
VOption *
VOption::set( const char *name, int value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, G_TYPE_INT );
g_value_set_int( &pair->value, value );
options.push_back( pair );
return( this );
}
// input double
VOption *
VOption::set( const char *name, double value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, G_TYPE_DOUBLE );
g_value_set_double( &pair->value, value );
options.push_back( pair );
return( this );
}
VOption *
VOption::set( const char *name, const char *value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, G_TYPE_STRING );
g_value_set_string( &pair->value, value );
options.push_back( pair );
return( this );
}
// input image
VOption *
VOption::set( const char *name, VImage value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_IMAGE );
g_value_set_object( &pair->value, value.get_image() );
options.push_back( pair );
return( this );
}
// input double array
VOption *
VOption::set( const char *name, std::vector<double> value )
{
Pair *pair = new Pair( name );
double *array;
unsigned int i;
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
vips_value_set_array_double( &pair->value, NULL,
static_cast< int >( value.size() ) );
array = vips_value_get_array_double( &pair->value, NULL );
for( i = 0; i < value.size(); i++ )
array[i] = value[i];
options.push_back( pair );
return( this );
}
// input image array
VOption *
VOption::set( const char *name, std::vector<VImage> value )
{
Pair *pair = new Pair( name );
VipsImage **array;
unsigned int i;
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_ARRAY_IMAGE );
vips_value_set_array_image( &pair->value,
static_cast< int >( value.size() ) );
array = vips_value_get_array_image( &pair->value, NULL );
for( i = 0; i < value.size(); i++ ) {
VipsImage *vips_image = value[i].get_image();
array[i] = vips_image;
g_object_ref( vips_image );
}
options.push_back( pair );
return( this );
}
// input blob
VOption *
VOption::set( const char *name, VipsBlob *value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_BLOB );
g_value_set_boxed( &pair->value, value );
options.push_back( pair );
return( this );
}
// output bool
VOption *
VOption::set( const char *name, bool *value )
{
Pair *pair = new Pair( name );
pair->input = false;
pair->vbool = value;
g_value_init( &pair->value, G_TYPE_BOOLEAN );
options.push_back( pair );
return( this );
}
// output int
VOption *
VOption::set( const char *name, int *value )
{
Pair *pair = new Pair( name );
pair->input = false;
pair->vint = value;
g_value_init( &pair->value, G_TYPE_INT );
options.push_back( pair );
return( this );
}
// output double
VOption *
VOption::set( const char *name, double *value )
{
Pair *pair = new Pair( name );
pair->input = false;
pair->vdouble = value;
g_value_init( &pair->value, G_TYPE_DOUBLE );
options.push_back( pair );
return( this );
}
// output image
VOption *
VOption::set( const char *name, VImage *value )
{
Pair *pair = new Pair( name );
pair->input = false;
pair->vimage = value;
g_value_init( &pair->value, VIPS_TYPE_IMAGE );
options.push_back( pair );
return( this );
}
// output doublearray
VOption *
VOption::set( const char *name, std::vector<double> *value )
{
Pair *pair = new Pair( name );
pair->input = false;
pair->vvector = value;
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
options.push_back( pair );
return( this );
}
// output blob
VOption *
VOption::set( const char *name, VipsBlob **value )
{
Pair *pair = new Pair( name );
pair->input = false;
pair->vblob = value;
g_value_init( &pair->value, VIPS_TYPE_BLOB );
options.push_back( pair );
return( this );
}
// just g_object_set_property(), except we allow set enum from string
static void
set_property( VipsObject *object, const char *name, const GValue *value )
{
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
GType type = G_VALUE_TYPE( value );
GParamSpec *pspec;
VipsArgumentClass *argument_class;
VipsArgumentInstance *argument_instance;
if( vips_object_get_argument( object, name,
&pspec, &argument_class, &argument_instance ) ) {
vips_warn( NULL, "%s", vips_error_buffer() );
vips_error_clear();
return;
}
if( G_IS_PARAM_SPEC_ENUM( pspec ) &&
type == G_TYPE_STRING ) {
GType pspec_type = G_PARAM_SPEC_VALUE_TYPE( pspec );
int enum_value;
GValue value2 = { 0 };
if( (enum_value = vips_enum_from_nick( object_class->nickname,
pspec_type, g_value_get_string( value ) )) < 0 ) {
vips_warn( NULL, "%s", vips_error_buffer() );
vips_error_clear();
return;
}
g_value_init( &value2, pspec_type );
g_value_set_enum( &value2, enum_value );
g_object_set_property( G_OBJECT( object ), name, &value2 );
g_value_unset( &value2 );
}
else
g_object_set_property( G_OBJECT( object ), name, value );
}
// walk the options and set props on the operation
void
VOption::set_operation( VipsOperation *operation )
{
std::list<Pair *>::iterator i;
for( i = options.begin(); i != options.end(); ++i )
if( (*i)->input ) {
#ifdef VIPS_DEBUG_VERBOSE
printf( "set_operation: " );
vips_object_print_name( VIPS_OBJECT( operation ) );
char *str_value = g_strdup_value_contents( &(*i)->value );
printf( ".%s = %s\n", (*i)->name, str_value );
g_free( str_value );
#endif /*VIPS_DEBUG_VERBOSE*/
set_property( VIPS_OBJECT( operation ),
(*i)->name, &(*i)->value );
}
}
// walk the options and fetch any requested outputs
void
VOption::get_operation( VipsOperation *operation )
{
std::list<Pair *>::iterator i;
for( i = options.begin(); i != options.end(); ++i )
if( ! (*i)->input ) {
const char *name = (*i)->name;
g_object_get_property( G_OBJECT( operation ),
name, &(*i)->value );
#ifdef VIPS_DEBUG_VERBOSE
printf( "get_operation: " );
vips_object_print_name( VIPS_OBJECT( operation ) );
char *str_value = g_strdup_value_contents(
&(*i)->value );
printf( ".%s = %s\n", name, str_value );
g_free( str_value );
#endif /*VIPS_DEBUG_VERBOSE*/
GValue *value = &(*i)->value;
GType type = G_VALUE_TYPE( value );
if( type == VIPS_TYPE_IMAGE ) {
// rebox object
VipsImage *image = VIPS_IMAGE(
g_value_get_object( value ) );
*((*i)->vimage) = VImage( image );
}
else if( type == G_TYPE_INT )
*((*i)->vint) = g_value_get_int( value );
else if( type == G_TYPE_BOOLEAN )
*((*i)->vbool) = g_value_get_boolean( value );
else if( type == G_TYPE_DOUBLE )
*((*i)->vdouble) = g_value_get_double( value );
else if( type == VIPS_TYPE_ARRAY_DOUBLE ) {
int length;
double *array =
vips_value_get_array_double( value,
&length );
int j;
((*i)->vvector)->resize( length );
for( j = 0; j < length; j++ )
(*((*i)->vvector))[j] = array[j];
}
else if( type == VIPS_TYPE_BLOB ) {
// our caller gets a reference
*((*i)->vblob) =
(VipsBlob *) g_value_dup_boxed( value );
}
}
}
void
VImage::call_option_string( const char *operation_name,
const char *option_string, VOption *options )
{
VipsOperation *operation;
VIPS_DEBUG_MSG( "vips_call_by_name: starting for %s ...\n",
operation_name );
if( !(operation = vips_operation_new( operation_name )) ) {
if( options )
delete options;
throw( VError() );
}
/* Set str options before vargs options, so the user can't
* override things we set deliberately.
*/
if( option_string &&
vips_object_set_from_string( VIPS_OBJECT( operation ),
option_string ) ) {
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
g_object_unref( operation );
delete options;
throw( VError() );
}
if( options )
options->set_operation( operation );
/* Build from cache.
*/
if( vips_cache_operation_buildp( &operation ) ) {
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
delete options;
throw( VError() );
}
/* Walk args again, writing output.
*/
if( options )
options->get_operation( operation );
/* We're done with options!
*/
delete options;
/* The operation we have built should now have been reffed by
* one of its arguments or have finished its work. Either
* way, we can unref.
*/
g_object_unref( operation );
}
void
VImage::call( const char *operation_name, VOption *options )
{
call_option_string( operation_name, NULL, options );
}
VImage
VImage::new_from_file( const char *name, VOption *options )
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
const char *operation_name;
VImage out;
vips__filename_split8( name, filename, option_string );
if( !(operation_name = vips_foreign_find_load( filename )) ) {
delete options;
throw VError();
}
call_option_string( operation_name, option_string,
(options ? options : VImage::option())->
set( "filename", filename )->
set( "out", &out ) );
return( out );
}
VImage
VImage::new_from_buffer( void *buf, size_t len, const char *option_string,
VOption *options )
{
const char *operation_name;
VipsBlob *blob;
VImage out;
if( !(operation_name = vips_foreign_find_load_buffer( buf, len )) ) {
delete options;
throw( VError() );
}
/* We don't take a copy of the data or free it.
*/
blob = vips_blob_new( NULL, buf, len );
options = (options ? options : VImage::option())->
set( "buffer", blob )->
set( "out", &out );
vips_area_unref( VIPS_AREA( blob ) );
call_option_string( operation_name, option_string, options );
return( out );
}
VImage
VImage::new_from_image( std::vector<double> pixel )
{
VImage onepx = VImage::black( 1, 1,
VImage::option()->set( "bands", bands() ) );
onepx = onepx.linear( to_vectorv( 1, 1.0 ), pixel ).cast( format() );
VImage big = onepx.embed( 0, 0, width(), height(),
VImage::option()->set( "extend", VIPS_EXTEND_COPY ) );
big = big.copy(
VImage::option()->
set( "interpretation", interpretation() )->
set( "xres", xres() )->
set( "yres", yres() )->
set( "xoffset", xres() )->
set( "yoffset", yres() ) );
return( big );
}
VImage
VImage::new_from_image( double pixel )
{
return( new_from_image( to_vectorv( 1, pixel ) ) );
}
VImage
VImage::new_matrix( int width, int height )
{
return( VImage( vips_image_new_matrix( width, height ) ) );
}
VImage
VImage::new_matrixv( int width, int height, ... )
{
VImage matrix = new_matrix( width, height );
VipsImage *vips_matrix = matrix.get_image();
va_list ap;
va_start( ap, height );
for( int y = 0; y < height; y++ )
for( int x = 0; x < width; x++ )
*VIPS_MATRIX( vips_matrix, x, y ) =
va_arg( ap, double );
va_end( ap );
return( matrix );
}
void
VImage::write_to_file( const char *name, VOption *options )
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
const char *operation_name;
vips__filename_split8( name, filename, option_string );
if( !(operation_name = vips_foreign_find_save( filename )) ) {
delete options;
throw VError();
}
call_option_string( operation_name, option_string,
(options ? options : VImage::option())->
set( "in", *this )->
set( "filename", filename ) );
}
void
VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
VOption *options )
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
const char *operation_name;
VipsBlob *blob;
vips__filename_split8( suffix, filename, option_string );
if( !(operation_name = vips_foreign_find_save_buffer( filename )) ) {
delete options;
throw VError();
}
call_option_string( operation_name, option_string,
(options ? options : VImage::option())->
set( "in", *this )->
set( "buffer", &blob ) );
if( blob ) {
if( buf ) {
*buf = VIPS_AREA( blob )->data;
VIPS_AREA( blob )->free_fn = NULL;
}
if( size )
*size = VIPS_AREA( blob )->length;
vips_area_unref( VIPS_AREA( blob ) );
}
}
#include "vips-operators.cpp"
std::vector<VImage>
VImage::bandsplit( VOption *options )
{
std::vector<VImage> b;
for( int i = 0; i < bands(); i++ )
b.push_back( extract_band( i ) );
return( b );
}
VImage
VImage::bandjoin( VImage other, VOption *options )
{
VImage v[2] = { *this, other };
std::vector<VImage> vec( v, v + VIPS_NUMBER( v ) );
return( bandjoin( vec, options ) );
}
std::complex<double>
VImage::minpos( VOption *options )
{
double x, y;
(void) min(
(options ? options : VImage::option()) ->
set( "x", &x ) ->
set( "y", &y ) );
return( std::complex<double>( x, y ) );
}
std::complex<double>
VImage::maxpos( VOption *options )
{
double x, y;
(void) max(
(options ? options : VImage::option()) ->
set( "x", &x ) ->
set( "y", &y ) );
return( std::complex<double>( x, y ) );
}
VIPS_NAMESPACE_END

View File

@@ -0,0 +1,76 @@
/* Object part of VInterpolate class
*/
/*
Copyright (C) 1991-2001 The National Gallery
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <vips/vips8>
#include <vips/debug.h>
/*
#define VIPS_DEBUG
#define VIPS_DEBUG_VERBOSE
*/
VIPS_NAMESPACE_START
VInterpolate
VInterpolate::new_from_name( const char *name, VOption *options )
{
VipsInterpolate *interp;
if( !(interp = vips_interpolate_new( name )) ) {
delete options;
throw VError();
}
delete options;
VInterpolate out( interp );
return( out );
}
VOption *
VOption::set( const char *name, VInterpolate value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_INTERPOLATE );
g_value_set_object( &pair->value, value.get_interpolate() );
options.push_back( pair );
return( this );
}
VIPS_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
#include <node.h> #include <node.h>
#include <vips/vips.h> #include <vips/vips8>
#include "nan.h" #include "nan.h"
@@ -29,9 +29,12 @@ using Nan::NewBuffer;
using Nan::Null; using Nan::Null;
using Nan::Error; using Nan::Error;
using vips::VImage;
using vips::VError;
using sharp::ImageType; using sharp::ImageType;
using sharp::ImageTypeId;
using sharp::DetermineImageType; using sharp::DetermineImageType;
using sharp::InitImage;
using sharp::HasProfile; using sharp::HasProfile;
using sharp::HasAlpha; using sharp::HasAlpha;
using sharp::ExifOrientation; using sharp::ExifOrientation;
@@ -81,13 +84,14 @@ class MetadataWorker : public AsyncWorker {
g_atomic_int_dec_and_test(&counterQueue); g_atomic_int_dec_and_test(&counterQueue);
ImageType imageType = ImageType::UNKNOWN; ImageType imageType = ImageType::UNKNOWN;
VipsImage *image = nullptr; VImage image;
if (baton->bufferInLength > 0) { if (baton->bufferInLength > 0) {
// From buffer // From buffer
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength); imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (imageType != ImageType::UNKNOWN) { if (imageType != ImageType::UNKNOWN) {
image = InitImage(baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM); try {
if (image == nullptr) { image = VImage::new_from_buffer(baton->bufferIn, baton->bufferInLength, nullptr);
} catch (...) {
(baton->err).append("Input buffer has corrupt header"); (baton->err).append("Input buffer has corrupt header");
imageType = ImageType::UNKNOWN; imageType = ImageType::UNKNOWN;
} }
@@ -98,57 +102,44 @@ class MetadataWorker : public AsyncWorker {
// From file // From file
imageType = DetermineImageType(baton->fileIn.data()); imageType = DetermineImageType(baton->fileIn.data());
if (imageType != ImageType::UNKNOWN) { if (imageType != ImageType::UNKNOWN) {
image = InitImage(baton->fileIn.data(), VIPS_ACCESS_RANDOM); try {
if (image == nullptr) { image = VImage::new_from_file(baton->fileIn.data());
} catch (...) {
(baton->err).append("Input file has corrupt header"); (baton->err).append("Input file has corrupt header");
imageType = ImageType::UNKNOWN; imageType = ImageType::UNKNOWN;
} }
} else { } else {
(baton->err).append("Input file is of an unsupported image format"); (baton->err).append("Input file is missing or of an unsupported image format");
} }
} }
if (image != nullptr && imageType != ImageType::UNKNOWN) { if (imageType != ImageType::UNKNOWN) {
// Image type // Image type
switch (imageType) { baton->format = ImageTypeId(imageType);
case ImageType::JPEG: baton->format = "jpeg"; break;
case ImageType::PNG: baton->format = "png"; break;
case ImageType::WEBP: baton->format = "webp"; break;
case ImageType::TIFF: baton->format = "tiff"; break;
case ImageType::MAGICK: baton->format = "magick"; break;
case ImageType::OPENSLIDE: baton->format = "openslide"; break;
case ImageType::UNKNOWN: break;
}
// VipsImage attributes // VipsImage attributes
baton->width = image->Xsize; baton->width = image.width();
baton->height = image->Ysize; baton->height = image.height();
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type); baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation());
baton->channels = image->Bands; baton->channels = image.bands();
baton->hasProfile = HasProfile(image); baton->hasProfile = HasProfile(image);
// Derived attributes // Derived attributes
baton->hasAlpha = HasAlpha(image); baton->hasAlpha = HasAlpha(image);
baton->orientation = ExifOrientation(image); baton->orientation = ExifOrientation(image);
// EXIF // EXIF
if (vips_image_get_typeof(image, VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) { if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
void* exif;
size_t exifLength; size_t exifLength;
if (!vips_image_get_blob(image, VIPS_META_EXIF_NAME, &exif, &exifLength)) { void const *exif = image.get_blob(VIPS_META_EXIF_NAME, &exifLength);
baton->exifLength = exifLength; baton->exif = static_cast<char*>(g_malloc(exifLength));
baton->exif = static_cast<char*>(g_malloc(exifLength)); memcpy(baton->exif, exif, exifLength);
memcpy(baton->exif, exif, exifLength); baton->exifLength = exifLength;
}
} }
// ICC profile // ICC profile
if (vips_image_get_typeof(image, VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) { if (image.get_typeof(VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
void* icc;
size_t iccLength; size_t iccLength;
if (!vips_image_get_blob(image, VIPS_META_ICC_NAME, &icc, &iccLength)) { void const *icc = image.get_blob(VIPS_META_ICC_NAME, &iccLength);
baton->iccLength = iccLength; baton->icc = static_cast<char*>(g_malloc(iccLength));
baton->icc = static_cast<char*>(g_malloc(iccLength)); memcpy(baton->icc, icc, iccLength);
memcpy(baton->icc, icc, iccLength); baton->iccLength = iccLength;
}
} }
// Drop image reference
g_object_unref(image);
} }
// Clean up // Clean up
vips_error_clear(); vips_error_clear();

0
src/metadata.h Executable file → Normal file
View File

View File

@@ -1,67 +1,38 @@
#include <vips/vips.h> #include <vips/vips8>
#include "common.h" #include "common.h"
#include "operations.h" #include "operations.h"
using vips::VImage;
namespace sharp { namespace sharp {
/* /*
Alpha composite src over dst Alpha composite src over dst
Assumes alpha channels are already premultiplied and will be unpremultiplied after Assumes alpha channels are already premultiplied and will be unpremultiplied after
*/ */
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out) { VImage Composite(VImage src, VImage dst) {
using sharp::HasAlpha; using sharp::HasAlpha;
// Split src into non-alpha and alpha // Split src into non-alpha and alpha
VipsImage *srcWithoutAlpha; VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
if (vips_extract_band(src, &srcWithoutAlpha, 0, "n", src->Bands - 1, nullptr)) VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
return -1;
vips_object_local(context, srcWithoutAlpha);
VipsImage *srcAlpha;
if (vips_extract_band(src, &srcAlpha, src->Bands - 1, "n", 1, nullptr))
return -1;
vips_object_local(context, srcAlpha);
// Split dst into non-alpha and alpha channels // Split dst into non-alpha and alpha channels
VipsImage *dstWithoutAlpha; VImage dstWithoutAlpha;
VipsImage *dstAlpha; VImage dstAlpha;
if (HasAlpha(dst)) { if (HasAlpha(dst)) {
// Non-alpha: extract all-but-last channel // Non-alpha: extract all-but-last channel
if (vips_extract_band(dst, &dstWithoutAlpha, 0, "n", dst->Bands - 1, nullptr)) { dstWithoutAlpha = dst.extract_band(0, VImage::option()->set("n", dst.bands() - 1));
return -1;
}
vips_object_local(context, dstWithoutAlpha);
// Alpha: Extract last channel // Alpha: Extract last channel
if (vips_extract_band(dst, &dstAlpha, dst->Bands - 1, "n", 1, nullptr)) { dstAlpha = dst[dst.bands() - 1] * (1.0 / 255.0);
return -1;
}
vips_object_local(context, dstAlpha);
} else { } else {
// Non-alpha: Copy reference // Non-alpha: Copy reference
dstWithoutAlpha = dst; dstWithoutAlpha = dst;
// Alpha: Use blank, opaque (0xFF) image // Alpha: Use blank, opaque (0xFF) image
VipsImage *black; dstAlpha = VImage::black(dst.width(), dst.height()).invert();
if (vips_black(&black, dst->Xsize, dst->Ysize, nullptr)) {
return -1;
}
vips_object_local(context, black);
if (vips_invert(black, &dstAlpha, nullptr)) {
return -1;
}
vips_object_local(context, dstAlpha);
} }
// Compute normalized input alpha channels:
VipsImage *srcAlphaNormalized;
if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
return -1;
vips_object_local(context, srcAlphaNormalized);
VipsImage *dstAlphaNormalized;
if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
return -1;
vips_object_local(context, dstAlphaNormalized);
// //
// Compute normalized output alpha channel: // Compute normalized output alpha channel:
// //
@@ -72,22 +43,8 @@ namespace sharp {
// out_a = src_a + dst_a * (1 - src_a) // out_a = src_a + dst_a * (1 - src_a)
// ^^^^^^^^^^^ // ^^^^^^^^^^^
// t0 // t0
// ^^^^^^^^^^^^^^^^^^^ VImage t0 = srcAlpha.linear(-1.0, 1.0);
// t1 VImage outAlphaNormalized = srcAlpha + dstAlpha * t0;
VipsImage *t0;
if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, nullptr))
return -1;
vips_object_local(context, t0);
VipsImage *t1;
if (vips_multiply(dstAlphaNormalized, t0, &t1, nullptr))
return -1;
vips_object_local(context, t1);
VipsImage *outAlphaNormalized;
if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, nullptr))
return -1;
vips_object_local(context, outAlphaNormalized);
// //
// Compute output RGB channels: // Compute output RGB channels:
@@ -101,182 +58,100 @@ namespace sharp {
// premultiplied RGBA image as reversal of premultiplication is handled // premultiplied RGBA image as reversal of premultiplication is handled
// externally. // externally.
// //
VipsImage *t2; VImage outRGBPremultiplied = srcWithoutAlpha + dstWithoutAlpha * t0;
if (vips_multiply(dstWithoutAlpha, t0, &t2, nullptr))
return -1;
vips_object_local(context, t2);
VipsImage *outRGBPremultiplied;
if (vips_add(srcWithoutAlpha, t2, &outRGBPremultiplied, nullptr))
return -1;
vips_object_local(context, outRGBPremultiplied);
// Denormalize output alpha channel:
VipsImage *outAlpha;
if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, nullptr))
return -1;
vips_object_local(context, outAlpha);
// Combine RGB and alpha channel into output image: // Combine RGB and alpha channel into output image:
return vips_bandjoin2(outRGBPremultiplied, outAlpha, out, nullptr); return outRGBPremultiplied.bandjoin(outAlphaNormalized * 255.0);
} }
/* /*
* Stretch luminance to cover full dynamic range. * Stretch luminance to cover full dynamic range.
*/ */
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) { VImage Normalize(VImage image) {
// Get original colourspace // Get original colourspace
VipsInterpretation typeBeforeNormalize = image->Type; VipsInterpretation typeBeforeNormalize = image.interpretation();
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) { if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
typeBeforeNormalize = VIPS_INTERPRETATION_sRGB; typeBeforeNormalize = VIPS_INTERPRETATION_sRGB;
} }
// Convert to LAB colourspace // Convert to LAB colourspace
VipsImage *lab; VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, nullptr)) {
return -1;
}
vips_object_local(context, lab);
// Extract luminance // Extract luminance
VipsImage *luminance; VImage luminance = lab[0];
if (vips_extract_band(lab, &luminance, 0, "n", 1, nullptr)) {
return -1;
}
vips_object_local(context, luminance);
// Extract chroma
VipsImage *chroma;
if (vips_extract_band(lab, &chroma, 1, "n", 2, nullptr)) {
return -1;
}
vips_object_local(context, chroma);
// Find luminance range // Find luminance range
VipsImage *stats; VImage stats = luminance.stats();
if (vips_stats(luminance, &stats, nullptr)) { double min = stats(0, 0)[0];
return -1; double max = stats(1, 0)[0];
}
vips_object_local(context, stats);
double min = *VIPS_MATRIX(stats, 0, 0);
double max = *VIPS_MATRIX(stats, 1, 0);
if (min != max) { if (min != max) {
// Extract chroma
VImage chroma = lab.extract_band(1, VImage::option()->set("n", 2));
// Calculate multiplication factor and addition
double f = 100.0 / (max - min); double f = 100.0 / (max - min);
double a = -(min * f); double a = -(min * f);
// Scale luminance // Scale luminance, join to chroma, convert back to original colourspace
VipsImage *luminance100; VImage normalized = luminance.linear(f, a).bandjoin(chroma).colourspace(typeBeforeNormalize);
if (vips_linear1(luminance, &luminance100, f, a, nullptr)) {
return -1;
}
vips_object_local(context, luminance100);
// Join scaled luminance to chroma
VipsImage *normalizedLab;
if (vips_bandjoin2(luminance100, chroma, &normalizedLab, nullptr)) {
return -1;
}
vips_object_local(context, normalizedLab);
// Convert to original colourspace
VipsImage *normalized;
if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, nullptr)) {
return -1;
}
vips_object_local(context, normalized);
// Attach original alpha channel, if any // Attach original alpha channel, if any
if (HasAlpha(image)) { if (HasAlpha(image)) {
// Extract original alpha channel // Extract original alpha channel
VipsImage *alpha; VImage alpha = image[image.bands() - 1];
if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, nullptr)) {
return -1;
}
vips_object_local(context, alpha);
// Join alpha channel to normalised image // Join alpha channel to normalised image
VipsImage *normalizedAlpha; return normalized.bandjoin(alpha);
if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, nullptr)) {
return -1;
}
vips_object_local(context, normalizedAlpha);
*out = normalizedAlpha;
} else { } else {
*out = normalized; return normalized;
} }
} else {
// Cannot normalise zero-range image
*out = image;
} }
return 0; return image;
}
/*
* Gamma encoding/decoding
*/
VImage Gamma(VImage image, double const exponent) {
if (HasAlpha(image)) {
// Separate alpha channel
VImage imageWithoutAlpha = image.extract_band(0,
VImage::option()->set("n", image.bands() - 1));
VImage alpha = image[image.bands() - 1];
return imageWithoutAlpha.gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
} else {
return image.gamma(VImage::option()->set("exponent", exponent));
}
} }
/* /*
* Gaussian blur (use sigma <0 for fast blur) * Gaussian blur (use sigma <0 for fast blur)
*/ */
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma) { VImage Blur(VImage image, double const sigma) {
VipsImage *blurred;
if (sigma < 0.0) { if (sigma < 0.0) {
// Fast, mild blur - averages neighbouring pixels // Fast, mild blur - averages neighbouring pixels
VipsImage *blur = vips_image_new_matrixv(3, 3, VImage blur = VImage::new_matrixv(3, 3,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0); 1.0, 1.0, 1.0);
vips_image_set_double(blur, "scale", 9); blur.set("scale", 9.0);
vips_object_local(context, blur); return image.conv(blur);
if (vips_conv(image, &blurred, blur, nullptr)) {
return -1;
}
} else { } else {
// Slower, accurate Gaussian blur // Slower, accurate Gaussian blur
// Create Gaussian function for standard deviation return image.gaussblur(sigma);
VipsImage *gaussian;
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, nullptr)) {
return -1;
}
vips_object_local(context, gaussian);
// Apply Gaussian function
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, nullptr)) {
return -1;
}
} }
vips_object_local(context, blurred);
*out = blurred;
return 0;
} }
/* /*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen. * Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
*/ */
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged) { VImage Sharpen(VImage image, int const radius, double const flat, double const jagged) {
VipsImage *sharpened;
if (radius == -1) { if (radius == -1) {
// Fast, mild sharpen // Fast, mild sharpen
VipsImage *sharpen = vips_image_new_matrixv(3, 3, VImage sharpen = VImage::new_matrixv(3, 3,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
-1.0, 32.0, -1.0, -1.0, 32.0, -1.0,
-1.0, -1.0, -1.0); -1.0, -1.0, -1.0);
vips_image_set_double(sharpen, "scale", 24); sharpen.set("scale", 24.0);
vips_object_local(context, sharpen); return image.conv(sharpen);
if (vips_conv(image, &sharpened, sharpen, nullptr)) {
return -1;
}
} 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
if (vips_sharpen(image, &sharpened, "radius", radius, "m1", flat, "m2", jagged, nullptr)) { return image.sharpen(
return -1; VImage::option()->set("radius", radius)->set("m1", flat)->set("m2", jagged)
} );
} }
vips_object_local(context, sharpened);
*out = sharpened;
return 0;
}
int Threshold(VipsObject *context, VipsImage *image, VipsImage **out, int threshold) {
VipsImage *greyscale;
if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, nullptr)) {
return -1;
}
vips_object_local(context, greyscale);
image = greyscale;
VipsImage *thresholded;
if (vips_moreeq_const1(image, &thresholded, threshold, nullptr)) {
return -1;
}
vips_object_local(context, thresholded);
*out = thresholded;
return 0;
} }
} // namespace sharp } // namespace sharp

23
src/operations.h Executable file → Normal file
View File

@@ -1,34 +1,37 @@
#ifndef SRC_OPERATIONS_H_ #ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_ #define SRC_OPERATIONS_H_
#include <vips/vips8>
using vips::VImage;
namespace sharp { namespace sharp {
/* /*
Composite images `src` and `dst` with premultiplied alpha channel and output Composite images `src` and `dst` with premultiplied alpha channel and output
image with premultiplied alpha. image with premultiplied alpha.
*/ */
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out); VImage Composite(VImage src, VImage dst);
/* /*
* Stretch luminance to cover full dynamic range. * Stretch luminance to cover full dynamic range.
*/ */
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out); VImage Normalize(VImage image);
/*
* Gamma encoding/decoding
*/
VImage Gamma(VImage image, double const exponent);
/* /*
* Gaussian blur. Use sigma of -1 for fast blur. * Gaussian blur. Use sigma of -1 for fast blur.
*/ */
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double 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 radius of -1 for fast sharpen.
*/ */
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged); VImage Sharpen(VImage image, int const radius, double const flat, double const jagged);
/*
* Perform thresholding on an image. If the image is not greyscale, will convert before thresholding.
* Pixels with a greyscale value greater-than-or-equal-to `threshold` will be pure white. All others will be pure black.
*/
int Threshold(VipsObject *context, VipsImage *image, VipsImage **out, int threshold);
} // namespace sharp } // namespace sharp
#endif // SRC_OPERATIONS_H_ #endif // SRC_OPERATIONS_H_

File diff suppressed because it is too large Load Diff

0
src/pipeline.h Executable file → Normal file
View File

View File

@@ -1,5 +1,5 @@
#include <node.h> #include <node.h>
#include <vips/vips.h> #include <vips/vips8>
#include "nan.h" #include "nan.h"
@@ -11,10 +11,6 @@
NAN_MODULE_INIT(init) { NAN_MODULE_INIT(init) {
vips_init("sharp"); vips_init("sharp");
// Set libvips operation cache limits
vips_cache_set_max_mem(100 * 1024 * 1024); // 100 MB
vips_cache_set_max(500); // 500 operations
// Methods available to JavaScript // Methods available to JavaScript
Nan::Set(target, Nan::New("metadata").ToLocalChecked(), Nan::Set(target, Nan::New("metadata").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked()); Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked());

View File

@@ -1,6 +1,6 @@
#include <cmath> #include <cmath>
#include <node.h> #include <node.h>
#include <vips/vips.h> #include <vips/vips8>
#include <vips/vector.h> #include <vips/vector.h>
#include "nan.h" #include "nan.h"
@@ -24,33 +24,49 @@ using Nan::To;
using Nan::Utf8String; using Nan::Utf8String;
/* /*
Get and set cache memory and item limits Get and set cache limits
*/ */
NAN_METHOD(cache) { NAN_METHOD(cache) {
HandleScope(); HandleScope();
// Set cache memory limit // Set memory limit
if (info[0]->IsInt32()) { if (info[0]->IsInt32()) {
vips_cache_set_max_mem(To<int32_t>(info[0]).FromJust() * 1048576); vips_cache_set_max_mem(To<int32_t>(info[0]).FromJust() * 1048576);
} }
// Set file limit
// Set cache items limit
if (info[1]->IsInt32()) { if (info[1]->IsInt32()) {
vips_cache_set_max(To<int32_t>(info[1]).FromJust()); vips_cache_set_max_files(To<int32_t>(info[1]).FromJust());
}
// Set items limit
if (info[2]->IsInt32()) {
vips_cache_set_max(To<int32_t>(info[2]).FromJust());
} }
// Get cache statistics // Get memory stats
Local<Object> cache = New<Object>(); Local<Object> memory = New<Object>();
Set(cache, New("current").ToLocalChecked(), Set(memory, New("current").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576))) New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576)))
); );
Set(cache, New("high").ToLocalChecked(), Set(memory, New("high").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576))) New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576)))
); );
Set(cache, New("memory").ToLocalChecked(), Set(memory, New("max").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576))) New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576)))
); );
Set(cache, New("items").ToLocalChecked(), New<Integer>(vips_cache_get_max())); // Get file stats
Local<Object> files = New<Object>();
Set(files, New("current").ToLocalChecked(), New<Integer>(vips_tracked_get_files()));
Set(files, New("max").ToLocalChecked(), New<Integer>(vips_cache_get_max_files()));
// Get item stats
Local<Object> items = New<Object>();
Set(items, New("current").ToLocalChecked(), New<Integer>(vips_cache_get_size()));
Set(items, New("max").ToLocalChecked(), New<Integer>(vips_cache_get_max()));
Local<Object> cache = New<Object>();
Set(cache, New("memory").ToLocalChecked(), memory);
Set(cache, New("files").ToLocalChecked(), files);
Set(cache, New("items").ToLocalChecked(), items);
info.GetReturnValue().Set(cache); info.GetReturnValue().Set(cache);
} }
@@ -156,19 +172,17 @@ NAN_METHOD(format) {
Local<String> rawId = New("raw").ToLocalChecked(); Local<String> rawId = New("raw").ToLocalChecked();
Set(raw, attrId, rawId); Set(raw, attrId, rawId);
Set(format, rawId, raw); Set(format, rawId, raw);
// No support for raw input yet, so always false Local<Boolean> supported = New<Boolean>(true);
Local<Boolean> unsupported = New<Boolean>(false); Local<Boolean> unsupported = New<Boolean>(false);
Local<Object> rawInput = New<Object>(); Local<Object> rawInput = New<Object>();
Set(rawInput, attrFile, unsupported); Set(rawInput, attrFile, unsupported);
Set(rawInput, attrBuffer, unsupported); Set(rawInput, attrBuffer, supported);
Set(rawInput, attrStream, unsupported); Set(rawInput, attrStream, supported);
Set(raw, attrInput, rawInput); Set(raw, attrInput, rawInput);
// Raw output via Buffer/Stream is available in libvips >= 7.42.0
Local<Boolean> hasOutputBufferRaw = New<Boolean>(vips_version(0) >= 8 || (vips_version(0) == 7 && vips_version(1) >= 42));
Local<Object> rawOutput = New<Object>(); Local<Object> rawOutput = New<Object>();
Set(rawOutput, attrFile, unsupported); Set(rawOutput, attrFile, unsupported);
Set(rawOutput, attrBuffer, hasOutputBufferRaw); Set(rawOutput, attrBuffer, supported);
Set(rawOutput, attrStream, hasOutputBufferRaw); Set(rawOutput, attrStream, supported);
Set(raw, attrOutput, rawOutput); Set(raw, attrOutput, rawOutput);
info.GetReturnValue().Set(format); info.GetReturnValue().Set(format);
@@ -180,101 +194,64 @@ NAN_METHOD(format) {
between two images of the same dimensions and number of channels. between two images of the same dimensions and number of channels.
*/ */
NAN_METHOD(_maxColourDistance) { NAN_METHOD(_maxColourDistance) {
using vips::VImage;
using vips::VError;
using sharp::DetermineImageType; using sharp::DetermineImageType;
using sharp::ImageType; using sharp::ImageType;
using sharp::InitImage;
using sharp::HasAlpha; using sharp::HasAlpha;
HandleScope(); HandleScope();
// Create "hook" VipsObject to hang image references from
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
// Open input files // Open input files
VipsImage *image1 = nullptr; VImage image1;
ImageType imageType1 = DetermineImageType(*Utf8String(info[0])); ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
if (imageType1 != ImageType::UNKNOWN) { if (imageType1 != ImageType::UNKNOWN) {
image1 = InitImage(*Utf8String(info[0]), VIPS_ACCESS_SEQUENTIAL); try {
if (image1 == nullptr) { image1 = VImage::new_from_file(*Utf8String(info[0]));
g_object_unref(hook); } catch (...) {
return ThrowError("Input file 1 has corrupt header"); return ThrowError("Input file 1 has corrupt header");
} else {
vips_object_local(hook, image1);
} }
} else { } else {
g_object_unref(hook);
return ThrowError("Input file 1 is of an unsupported image format"); return ThrowError("Input file 1 is of an unsupported image format");
} }
VipsImage *image2 = nullptr; VImage image2;
ImageType imageType2 = DetermineImageType(*Utf8String(info[1])); ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
if (imageType2 != ImageType::UNKNOWN) { if (imageType2 != ImageType::UNKNOWN) {
image2 = InitImage(*Utf8String(info[1]), VIPS_ACCESS_SEQUENTIAL); try {
if (image2 == nullptr) { image2 = VImage::new_from_file(*Utf8String(info[1]));
g_object_unref(hook); } catch (...) {
return ThrowError("Input file 2 has corrupt header"); return ThrowError("Input file 2 has corrupt header");
} else {
vips_object_local(hook, image2);
} }
} else { } else {
g_object_unref(hook);
return ThrowError("Input file 2 is of an unsupported image format"); return ThrowError("Input file 2 is of an unsupported image format");
} }
// Ensure same number of channels // Ensure same number of channels
if (image1->Bands != image2->Bands) { if (image1.bands() != image2.bands()) {
g_object_unref(hook);
return ThrowError("mismatchedBands"); return ThrowError("mismatchedBands");
} }
// Ensure same dimensions // Ensure same dimensions
if (image1->Xsize != image2->Xsize || image1->Ysize != image2->Ysize) { if (image1.width() != image2.width() || image1.height() != image2.height()) {
g_object_unref(hook);
return ThrowError("mismatchedDimensions"); return ThrowError("mismatchedDimensions");
} }
// Premultiply and remove alpha
if (HasAlpha(image1)) {
VipsImage *imagePremultiplied1;
if (vips_premultiply(image1, &imagePremultiplied1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultiplied1);
VipsImage *imagePremultipliedNoAlpha1;
if (vips_extract_band(image1, &imagePremultipliedNoAlpha1, 1, "n", image1->Bands - 1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultipliedNoAlpha1);
image1 = imagePremultipliedNoAlpha1;
}
if (HasAlpha(image2)) {
VipsImage *imagePremultiplied2;
if (vips_premultiply(image2, &imagePremultiplied2, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultiplied2);
VipsImage *imagePremultipliedNoAlpha2;
if (vips_extract_band(image2, &imagePremultipliedNoAlpha2, 1, "n", image2->Bands - 1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultipliedNoAlpha2);
image2 = imagePremultipliedNoAlpha2;
}
// Calculate colour distance
VipsImage *difference;
if (vips_dE00(image1, image2, &difference, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, difference);
// Extract maximum distance
double maxColourDistance; double maxColourDistance;
if (vips_max(difference, &maxColourDistance, nullptr)) { try {
g_object_unref(hook); // Premultiply and remove alpha
return ThrowError(vips_error_buffer()); if (HasAlpha(image1)) {
image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
}
if (HasAlpha(image2)) {
image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1));
}
// Calculate colour distance
maxColourDistance = image1.dE00(image2).max();
} catch (VError err) {
return ThrowError(err.what());
} }
g_object_unref(hook);
// Clean up libvips' per-request data and threads
vips_error_clear();
vips_thread_shutdown();
info.GetReturnValue().Set(New<Number>(maxColourDistance)); info.GetReturnValue().Set(New<Number>(maxColourDistance));
} }

View File

@@ -5,11 +5,11 @@
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"description": "Benchmark and performance tests for sharp", "description": "Benchmark and performance tests for sharp",
"scripts": { "scripts": {
"test": "node perf && node random && node parallel" "test": "VIPS_WARNING=0 node perf && node random && node parallel"
}, },
"devDependencies": { "devDependencies": {
"async": "^1.5.0", "async": "^1.5.2",
"benchmark": "^1.0.0", "benchmark": "^2.0.0",
"gm": "^1.21.0", "gm": "^1.21.0",
"imagemagick": "^0.1.3", "imagemagick": "^0.1.3",
"imagemagick-native": "elad/node-imagemagick-native", "imagemagick-native": "elad/node-imagemagick-native",

View File

@@ -34,7 +34,7 @@ var magickFilterBilinear = 'Triangle';
var magickFilterBicubic = 'Lanczos'; 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(0); sharp.cache(false);
// Enable use of SIMD // Enable use of SIMD
sharp.simd(true); sharp.simd(true);
@@ -535,7 +535,7 @@ async.series({
}).on('cycle', function(event) { }).on('cycle', function(event) {
console.log('jpeg-linear ' + String(event.target)); console.log('jpeg-linear ' + String(event.target));
}).on('complete', function() { }).on('complete', function() {
callback(null, this.filter('fastest').pluck('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
@@ -782,7 +782,7 @@ async.series({
}).on('cycle', function(event) { }).on('cycle', function(event) {
console.log('jpeg-cubic ' + String(event.target)); console.log('jpeg-cubic ' + String(event.target));
}).on('complete', function() { }).on('complete', function() {
callback(null, this.filter('fastest').pluck('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
@@ -882,7 +882,7 @@ async.series({
}).on('cycle', function(event) { }).on('cycle', function(event) {
console.log('interpolators ' + String(event.target)); console.log('interpolators ' + String(event.target));
}).on('complete', function() { }).on('complete', function() {
callback(null, this.filter('fastest').pluck('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
@@ -1115,7 +1115,7 @@ async.series({
pngSuite.on('cycle', function(event) { pngSuite.on('cycle', function(event) {
console.log(' png ' + String(event.target)); console.log(' png ' + String(event.target));
}).on('complete', function() { }).on('complete', function() {
callback(null, this.filter('fastest').pluck('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
@@ -1182,7 +1182,7 @@ async.series({
}).on('cycle', function(event) { }).on('cycle', function(event) {
console.log('webp ' + String(event.target)); console.log('webp ' + String(event.target));
}).on('complete', function() { }).on('complete', function() {
callback(null, this.filter('fastest').pluck('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
} }
}, function(err, results) { }, function(err, results) {

View File

@@ -70,7 +70,7 @@ new Benchmark.Suite('random').add('imagemagick', {
}).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').pluck('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()); console.dir(sharp.cache());
}).run(); }).run();

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
id="Wikimedia logo"
viewBox="-599 -599 1198 1198" width="1024" height="1024">
<defs>
<clipPath id="mask">
<path d="M 47.5,-87.5 v 425 h -95 v -425 l -552,-552 v 1250 h 1199 v -1250 z" />
</clipPath>
</defs>
<g clip-path="url(#mask)">
<circle id="green parts" fill="#396" r="336.5"/>
<circle id="blue arc" fill="none" stroke="#069" r="480.25" stroke-width="135.5" />
</g>
<circle fill="#900" cy="-379.5" r="184.5" id="red circle"/>
</svg>

Before

Width:  |  Height:  |  Size: 692 B

3
test/fixtures/check.svg vendored Normal file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M30,76q6-14,13-26q6-12,14-23q8-12,13-17q3-4,6-6q1-1,5-2q8-1,12-1q1,0,1,1q0,1-1,2q-13,11-27,33q-14,21-24,44q-4,9-5,11q-1,2-9,2q-5,0-6-1q-1-1-5-6q-5-8-12-15q-3-4-3-6q0-2,4-5q3-2,6-2q3,0,8,3q5,4,10,14z" fill="green" />
</svg>

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

BIN
test/fixtures/expected/svg1200.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

BIN
test/fixtures/expected/svg72.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 938 B

View File

@@ -84,7 +84,7 @@ 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
inputSvg: getPath('Wikimedia-logo.svg'), // http://commons.wikimedia.org/wiki/File:Wikimedia-logo.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 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

@@ -4,8 +4,6 @@ var assert = require('assert');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
var sharp = require('../../index'); var sharp = require('../../index');
sharp.cache(0);
describe('Alpha transparency', function() { describe('Alpha transparency', function() {
it('Flatten to black', function(done) { it('Flatten to black', function(done) {
@@ -62,14 +60,11 @@ describe('Alpha transparency', function() {
it('Do not flatten', function(done) { it('Do not flatten', function(done) {
sharp(fixtures.inputPngWithTransparency) sharp(fixtures.inputPngWithTransparency)
.flatten(false) .flatten(false)
.toBuffer(function(err, data) { .toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
sharp(data).metadata(function(err, metadata) { assert.strictEqual('png', info.format);
if (err) throw err; assert.strictEqual(4, info.channels);
assert.strictEqual('png', metadata.format); done();
assert.strictEqual(4, metadata.channels);
done();
});
}); });
}); });
@@ -77,14 +72,11 @@ describe('Alpha transparency', function() {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.background('#ff0000') .background('#ff0000')
.flatten() .flatten()
.toBuffer(function(err, data) { .toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
sharp(data).metadata(function(err, metadata) { assert.strictEqual('jpeg', info.format);
if (err) throw err; assert.strictEqual(3, info.channels);
assert.strictEqual('jpeg', metadata.format); done();
assert.strictEqual(3, metadata.channels);
done();
});
}); });
}); });
@@ -94,7 +86,6 @@ describe('Alpha transparency', function() {
var expected = fixtures.expected(BASE_NAME); var expected = fixtures.expected(BASE_NAME);
sharp(fixtures.inputPngAlphaPremultiplicationSmall) sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.resize(2048, 1536) .resize(2048, 1536)
.interpolateWith('bicubic')
.toFile(actual, function(err) { .toFile(actual, function(err) {
if (err) { if (err) {
done(err); done(err);
@@ -111,7 +102,6 @@ describe('Alpha transparency', function() {
var expected = fixtures.expected(BASE_NAME); var expected = fixtures.expected(BASE_NAME);
sharp(fixtures.inputPngAlphaPremultiplicationLarge) sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(1024, 768) .resize(1024, 768)
.interpolateWith('bicubic')
.toFile(actual, function(err) { .toFile(actual, function(err) {
if (err) { if (err) {
done(err); done(err);

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Blur', function() { describe('Blur', function() {
it('specific radius 1', function(done) { it('specific radius 1', function(done) {

9
test/unit/cache.js Normal file
View File

@@ -0,0 +1,9 @@
'use strict';
var sharp = require('../../index');
// Define SHARP_TEST_WITHOUT_CACHE environment variable to prevent use of libvips' cache
beforeEach(function() {
sharp.cache(process.env.SHARP_TEST_WITHOUT_CACHE ? false : true);
});

View File

@@ -6,9 +6,15 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Clone', function() { describe('Clone', function() {
beforeEach(function() {
sharp.cache(false);
});
afterEach(function() {
sharp.cache(true);
});
it('Read from Stream and write to multiple Streams', function(done) { it('Read from Stream and write to multiple Streams', function(done) {
var finishEventsExpected = 2; var finishEventsExpected = 2;
// Output stream 1 // Output stream 1
@@ -55,4 +61,5 @@ describe('Clone', function() {
// Go // Go
fs.createReadStream(fixtures.inputJpg).pipe(rotator); fs.createReadStream(fixtures.inputJpg).pipe(rotator);
}); });
}); });

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Colour space conversion', function() { describe('Colour space conversion', function() {
it('To greyscale', function(done) { it('To greyscale', function(done) {
@@ -31,7 +29,7 @@ describe('Colour space conversion', function() {
.toFile(fixtures.path('output.greyscale-not.jpg'), done); .toFile(fixtures.path('output.greyscale-not.jpg'), done);
}); });
if (sharp.format.webp.output.buffer) { if (sharp.format.tiff.input.file && sharp.format.webp.output.buffer) {
it('From 1-bit TIFF to sRGB WebP [slow]', function(done) { it('From 1-bit TIFF to sRGB WebP [slow]', function(done) {
sharp(fixtures.inputTiff) sharp(fixtures.inputTiff)
.webp() .webp()

View File

@@ -11,7 +11,9 @@ describe('cpplint', function() {
// Ignore cpplint failures, possibly newline-related, on Windows // Ignore cpplint failures, possibly newline-related, on Windows
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
// List C++ source files // List C++ source files
fs.readdirSync(path.join(__dirname, '..', '..', 'src')).forEach(function (source) { fs.readdirSync(path.join(__dirname, '..', '..', 'src')).filter(function(source) {
return source !== 'libvips';
}).forEach(function(source) {
var file = path.join('src', source); var file = path.join('src', source);
it(file, function(done) { it(file, function(done) {
// Lint each source file // Lint each source file

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Crop gravities', function() { describe('Crop gravities', function() {
var testSettings = [ var testSettings = [
@@ -147,4 +145,25 @@ describe('Crop gravities', function() {
sharp(fixtures.inputJpg).crop('yadda'); sharp(fixtures.inputJpg).crop('yadda');
}); });
}); });
it('does not throw if crop gravity is undefined', function() {
assert.doesNotThrow(function() {
sharp(fixtures.inputJpg).crop();
});
});
it('defaults crop gravity to sharp.gravity.center', function(done) {
var centerGravitySettings = testSettings.filter(function (settings) {
return settings.name === 'Center';
})[0];
sharp(fixtures.inputJpg)
.resize(centerGravitySettings.width, centerGravitySettings.height)
.crop()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(centerGravitySettings.width, info.width);
assert.strictEqual(centerGravitySettings.height, info.height);
fixtures.assertSimilar(fixtures.expected(centerGravitySettings.fixture), data, done);
});
});
}); });

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Embed', function() { describe('Embed', function() {
it('JPEG within PNG, no alpha channel', function(done) { it('JPEG within PNG, no alpha channel', function(done) {
@@ -20,11 +18,8 @@ describe('Embed', function() {
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
sharp(data).metadata(function(err, metadata) { assert.strictEqual(3, info.channels);
if (err) throw err; fixtures.assertSimilar(fixtures.expected('embed-3-into-3.png'), data, done);
assert.strictEqual(3, metadata.channels);
done();
});
}); });
}); });
@@ -41,11 +36,8 @@ describe('Embed', function() {
assert.strictEqual('webp', info.format); assert.strictEqual('webp', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
sharp(data).metadata(function(err, metadata) { assert.strictEqual(4, info.channels);
if (err) throw err; fixtures.assertSimilar(fixtures.expected('embed-3-into-4.webp'), data, done);
assert.strictEqual(4, metadata.channels);
done();
});
}); });
}); });
} }
@@ -60,11 +52,8 @@ describe('Embed', function() {
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(50, info.width); assert.strictEqual(50, info.width);
assert.strictEqual(50, info.height); assert.strictEqual(50, info.height);
sharp(data).metadata(function(err, metadata) { assert.strictEqual(4, info.channels);
if (err) throw err; fixtures.assertSimilar(fixtures.expected('embed-4-into-4.png'), data, done);
assert.strictEqual(4, metadata.channels);
done();
});
}); });
}); });
@@ -78,10 +67,27 @@ describe('Embed', function() {
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(32, info.width); assert.strictEqual(32, info.width);
assert.strictEqual(16, info.height); assert.strictEqual(16, info.height);
assert.strictEqual(4, info.channels);
fixtures.assertSimilar(fixtures.expected('embed-16bit.png'), data, done); fixtures.assertSimilar(fixtures.expected('embed-16bit.png'), data, done);
}); });
}); });
it('16-bit PNG with alpha channel onto RGBA', function(done) {
sharp(fixtures.inputPngWithTransparency16bit)
.resize(32, 16)
.embed()
.background({r: 0, g: 0, b: 0, a: 0})
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(32, info.width);
assert.strictEqual(16, info.height);
assert.strictEqual(4, info.channels);
fixtures.assertSimilar(fixtures.expected('embed-16bit-rgba.png'), data, done);
});
});
it('Enlarge and embed', function(done) { it('Enlarge and embed', function(done) {
sharp(fixtures.inputPngWithOneColor) sharp(fixtures.inputPngWithOneColor)
.embed() .embed()
@@ -92,6 +98,7 @@ describe('Embed', function() {
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
assert.strictEqual(3, info.channels);
fixtures.assertSimilar(fixtures.expected('embed-enlarge.png'), data, done); fixtures.assertSimilar(fixtures.expected('embed-enlarge.png'), data, done);
}); });
}); });

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Partial image extraction', function() { describe('Partial image extraction', function() {
describe('using the legacy extract(top,left,width,height) syntax', function () { describe('using the legacy extract(top,left,width,height) syntax', function () {
it('JPEG', function(done) { it('JPEG', function(done) {
@@ -67,17 +65,19 @@ describe('Partial image extraction', function() {
}); });
} }
it('TIFF', function(done) { if (sharp.format.tiff.output.file) {
sharp(fixtures.inputTiff) it('TIFF', function(done) {
.extract({ left: 34, top: 63, width: 341, height: 529 }) sharp(fixtures.inputTiff)
.jpeg() .extract({ left: 34, top: 63, width: 341, height: 529 })
.toBuffer(function(err, data, info) { .jpeg()
if (err) throw err; .toBuffer(function(err, data, info) {
assert.strictEqual(341, info.width); if (err) throw err;
assert.strictEqual(529, info.height); assert.strictEqual(341, info.width);
fixtures.assertSimilar(fixtures.expected('extract.tiff'), data, done); assert.strictEqual(529, info.height);
}); fixtures.assertSimilar(fixtures.expected('extract.tiff'), data, done);
}); });
});
}
it('Before resize', function(done) { it('Before resize', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Gamma correction', function() { describe('Gamma correction', function() {
it('value of 0.0 (disabled)', function(done) { it('value of 0.0 (disabled)', function(done) {
@@ -16,7 +14,7 @@ describe('Gamma correction', function() {
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width); assert.strictEqual(129, info.width);
assert.strictEqual(111, info.height); assert.strictEqual(111, info.height);
fixtures.assertSimilar(fixtures.expected('gamma-0.0.jpg'), data, done); fixtures.assertSimilar(fixtures.expected('gamma-0.0.jpg'), data, { threshold: 9 }, done);
}); });
}); });

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Interpolation', function() { describe('Interpolation', function() {
it('nearest neighbour', function(done) { it('nearest neighbour', function(done) {

View File

@@ -6,10 +6,15 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Input/output', function() { describe('Input/output', function() {
beforeEach(function() {
sharp.cache(false);
});
afterEach(function() {
sharp.cache(true);
});
it('Read from File and write to Stream', function(done) { it('Read from File and write to Stream', function(done) {
var writable = fs.createWriteStream(fixtures.outputJpg); var writable = fs.createWriteStream(fixtures.outputJpg);
writable.on('finish', function() { writable.on('finish', function() {
@@ -387,76 +392,91 @@ describe('Input/output', function() {
}); });
}); });
describe('Output filename without extension uses input format', function() { describe('Output filename with unknown extension', function() {
it('JPEG', function(done) { it('Match JPEG input', function(done) {
sharp(fixtures.inputJpg).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { sharp(fixtures.inputJpg)
if (err) throw err; .resize(320, 80)
assert.strictEqual(true, info.size > 0); .toFile(fixtures.outputZoinks, function(err, info) {
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
fs.unlinkSync(fixtures.outputZoinks);
done();
});
});
it('PNG', function(done) {
sharp(fixtures.inputPng).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
fs.unlinkSync(fixtures.outputZoinks);
done();
});
});
it('Transparent PNG', function(done) {
sharp(fixtures.inputPngWithTransparency).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
done();
});
});
if (sharp.format.webp.input.file) {
it('WebP', function(done) {
sharp(fixtures.inputWebP).resize(320, 80).toFile(fixtures.outputZoinks, 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('webp', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height); assert.strictEqual(80, info.height);
fs.unlinkSync(fixtures.outputZoinks); fs.unlinkSync(fixtures.outputZoinks);
done(); done();
}); });
});
it('Match PNG input', function(done) {
sharp(fixtures.inputPng)
.resize(320, 80)
.toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
fs.unlinkSync(fixtures.outputZoinks);
done();
});
});
if (sharp.format.webp.input.file) {
it('Match WebP input', function(done) {
sharp(fixtures.inputWebP)
.resize(320, 80)
.toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('webp', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
fs.unlinkSync(fixtures.outputZoinks);
done();
});
}); });
} }
it('TIFF', function(done) { if (sharp.format.tiff.input.file) {
sharp(fixtures.inputTiff).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { it('Match TIFF input', function(done) {
if (err) throw err; sharp(fixtures.inputTiff)
assert.strictEqual(true, info.size > 0); .resize(320, 80)
assert.strictEqual('tiff', info.format); .toFile(fixtures.outputZoinks, function(err, info) {
assert.strictEqual(320, info.width); if (err) throw err;
assert.strictEqual(80, info.height); assert.strictEqual(true, info.size > 0);
fs.unlinkSync(fixtures.outputZoinks); assert.strictEqual('tiff', info.format);
done(); assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
fs.unlinkSync(fixtures.outputZoinks);
done();
});
}); });
}
it('Match GIF input, therefore fail', function(done) {
sharp(fixtures.inputGif)
.resize(320, 80)
.toFile(fixtures.outputZoinks, function(err) {
assert(!!err);
done();
});
}); });
it('Fail with GIF', function(done) { it('Force JPEG format for PNG input', function(done) {
sharp(fixtures.inputGif).resize(320, 80).toFile(fixtures.outputZoinks, function(err) { sharp(fixtures.inputPng)
assert(!!err); .resize(320, 80)
done(); .jpeg()
}); .toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
fs.unlinkSync(fixtures.outputZoinks);
done();
});
}); });
}); });
describe('PNG output', function() { describe('PNG output', function() {
@@ -629,20 +649,37 @@ describe('Input/output', function() {
}); });
if (sharp.format.magick.input.file) { if (sharp.format.magick.input.file) {
it('Convert SVG, if supported, to PNG', function(done) { it('Convert SVG to PNG at default 72DPI', function(done) {
sharp(fixtures.inputSvg) sharp(fixtures.inputSvg)
.resize(100, 100) .resize(1024)
.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) {
assert.strictEqual(0, err.message.indexOf('Input file is of an unsupported image format')); assert.strictEqual(0, err.message.indexOf('Input file is missing or of an unsupported image format'));
done(); done();
} else { } else {
assert.strictEqual(true, info.size > 0);
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width); assert.strictEqual(40, info.width);
assert.strictEqual(100, info.height); assert.strictEqual(40, info.height);
fixtures.assertSimilar(fixtures.expected('svg.png'), data, done); fixtures.assertSimilar(fixtures.expected('svg72.png'), data, done);
}
});
});
it('Convert SVG to PNG at 300DPI', function(done) {
sharp(fixtures.inputSvg, { density: 1200 })
.resize(1024)
.extract({left: 290, top: 760, width: 40, height: 40})
.toFormat('png')
.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, done);
} }
}); });
}); });
@@ -819,6 +856,109 @@ describe('Input/output', function() {
}); });
describe('Input options', function() {
it('Non-Object options fails', function() {
assert.throws(function() {
sharp(null, 'zoinks');
});
});
it('Invalid density: string', function() {
assert.throws(function() {
sharp(null, { density: 'zoinks' } );
});
});
it('Invalid density: float', function() {
assert.throws(function() {
sharp(null, { density: 0.5 } );
});
});
it('Ignore unknown attribute', function() {
sharp(null, { unknown: true } );
});
});
describe('Raw pixel input', function() {
it('Missing options', function() {
assert.throws(function() {
sharp(null, { raw: {} } );
});
});
it('Incomplete options', function() {
assert.throws(function() {
sharp(null, { raw: { width: 1, height: 1} } );
});
});
it('Invalid channels', function() {
assert.throws(function() {
sharp(null, { raw: { width: 1, height: 1, channels: 5} } );
});
});
it('Invalid height', function() {
assert.throws(function() {
sharp(null, { raw: { width: 1, height: 0, channels: 4} } );
});
});
it('Invalid width', function() {
assert.throws(function() {
sharp(null, { raw: { width: 'zoinks', height: 1, channels: 4} } );
});
});
it('RGB', function(done) {
// Convert to raw pixel data
sharp(fixtures.inputJpg)
.resize(256)
.raw()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(256, info.width);
assert.strictEqual(209, info.height);
assert.strictEqual(3, info.channels);
// Convert back to JPEG
sharp(data, {
raw: {
width: info.width,
height: info.height,
channels: info.channels
}})
.jpeg()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(256, info.width);
assert.strictEqual(209, info.height);
assert.strictEqual(3, info.channels);
fixtures.assertSimilar(fixtures.inputJpg, data, done);
});
});
});
it('RGBA', function(done) {
// Convert to raw pixel data
sharp(fixtures.inputPngOverlayLayer1)
.resize(256)
.raw()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(256, info.width);
assert.strictEqual(192, info.height);
assert.strictEqual(4, info.channels);
// Convert back to PNG
sharp(data, {
raw: {
width: info.width,
height: info.height,
channels: info.channels
}})
.png()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(256, info.width);
assert.strictEqual(192, info.height);
assert.strictEqual(4, info.channels);
fixtures.assertSimilar(fixtures.inputPngOverlayLayer1, data, done);
});
});
});
});
it('Queue length change events', function(done) { it('Queue length change events', function(done) {
var eventCounter = 0; var eventCounter = 0;
var queueListener = function(queueLength) { var queueListener = function(queueLength) {

View File

@@ -8,8 +8,6 @@ var icc = require('icc');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Image metadata', function() { describe('Image metadata', function() {
it('JPEG', function(done) { it('JPEG', function(done) {
@@ -57,22 +55,24 @@ describe('Image metadata', function() {
}); });
}); });
it('TIFF', function(done) { if (sharp.format.tiff.input.file) {
sharp(fixtures.inputTiff).metadata(function(err, metadata) { it('TIFF', function(done) {
if (err) throw err; sharp(fixtures.inputTiff).metadata(function(err, metadata) {
assert.strictEqual('tiff', metadata.format); if (err) throw err;
assert.strictEqual(2464, metadata.width); assert.strictEqual('tiff', metadata.format);
assert.strictEqual(3248, metadata.height); assert.strictEqual(2464, metadata.width);
assert.strictEqual('b-w', metadata.space); assert.strictEqual(3248, metadata.height);
assert.strictEqual(1, metadata.channels); assert.strictEqual('b-w', metadata.space);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(1, metadata.channels);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual('undefined', typeof metadata.orientation); assert.strictEqual(false, metadata.hasAlpha);
assert.strictEqual('undefined', typeof metadata.exif); assert.strictEqual('undefined', typeof metadata.orientation);
assert.strictEqual('undefined', typeof metadata.icc); assert.strictEqual('undefined', typeof metadata.exif);
done(); assert.strictEqual('undefined', typeof metadata.icc);
done();
});
}); });
}); }
it('PNG', function(done) { it('PNG', function(done) {
sharp(fixtures.inputPng).metadata(function(err, metadata) { sharp(fixtures.inputPng).metadata(function(err, metadata) {
@@ -127,21 +127,23 @@ describe('Image metadata', function() {
}); });
} }
it('GIF via libmagick', function(done) { if (sharp.format.magick.input.file) {
sharp(fixtures.inputGif).metadata(function(err, metadata) { it('GIF via libmagick', function(done) {
if (err) throw err; sharp(fixtures.inputGif).metadata(function(err, metadata) {
assert.strictEqual('magick', metadata.format); if (err) throw err;
assert.strictEqual(800, metadata.width); assert.strictEqual('magick', metadata.format);
assert.strictEqual(533, metadata.height); assert.strictEqual(800, metadata.width);
assert.strictEqual(3, metadata.channels); assert.strictEqual(533, metadata.height);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(3, metadata.channels);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual('undefined', typeof metadata.orientation); assert.strictEqual(false, metadata.hasAlpha);
assert.strictEqual('undefined', typeof metadata.exif); assert.strictEqual('undefined', typeof metadata.orientation);
assert.strictEqual('undefined', typeof metadata.icc); assert.strictEqual('undefined', typeof metadata.exif);
done(); assert.strictEqual('undefined', typeof metadata.icc);
done();
});
}); });
}); }
if (sharp.format.openslide.input.file) { if (sharp.format.openslide.input.file) {
it('Aperio SVS via openslide', function(done) { it('Aperio SVS via openslide', function(done) {

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Negate', function() { describe('Negate', function() {
it('negate (jpeg)', function(done) { it('negate (jpeg)', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)

View File

@@ -5,109 +5,108 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0); var assertNormalized = function(data) {
var min = 255;
var max = 0;
for (var i = 0; i < data.length; i++) {
min = Math.min(min, data[i]);
max = Math.max(max, data[i]);
}
assert.strictEqual(0, min);
assert.strictEqual(255, max);
};
describe('Normalization', function () { describe('Normalization', function () {
it('uses the same prototype for both spellings', function () { it('uses the same prototype for both spellings', function() {
assert.strictEqual(sharp.prototype.normalize, sharp.prototype.normalise); assert.strictEqual(sharp.prototype.normalize, sharp.prototype.normalise);
}); });
// Normalize is currently unavailable on Windows it('spreads rgb image values between 0 and 255', function(done) {
if (process.platform !== 'win32') { sharp(fixtures.inputJpgWithLowContrast)
.normalize()
.raw()
.toBuffer(function (err, data, info) {
if (err) throw err;
assertNormalized(data);
done();
});
});
it('spreads rgb image values between 0 and 255', function(done) { it('spreads grayscaled image values between 0 and 255', function(done) {
sharp(fixtures.inputJpgWithLowContrast) sharp(fixtures.inputJpgWithLowContrast)
.normalize() .gamma()
.raw() .greyscale()
.toBuffer(function (err, data, info) { .normalize(true)
if (err) throw err; .raw()
var min = 255, max = 0, i; .toBuffer(function (err, data, info) {
for (i = 0; i < data.length; i += 3) { if (err) throw err;
min = Math.min(min, data[i], data[i + 1], data[i + 2]); assertNormalized(data);
max = Math.max(max, data[i], data[i + 1], data[i + 2]); done();
} });
assert.strictEqual(0, min); });
assert.strictEqual(255, max);
done();
});
});
it('spreads grayscaled image values between 0 and 255', function(done) { it('stretches greyscale images with alpha channel', function(done) {
sharp(fixtures.inputJpgWithLowContrast) sharp(fixtures.inputPngWithGreyAlpha)
.gamma() .normalize()
.greyscale() .raw()
.normalize(true) .toBuffer(function (err, data, info) {
.raw() if (err) throw err;
.toBuffer(function (err, data, info) { assertNormalized(data);
if (err) throw err; done();
var min = 255, max = 0, i; });
for (i = 0; i < data.length; i++) { });
min = Math.min(min, data[i]);
max = Math.max(max, data[i]);
}
assert.strictEqual(0, min);
assert.strictEqual(255, max);
done();
});
});
it('stretches greyscale images with alpha channel', function (done) { it('keeps an existing alpha channel', function(done) {
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithTransparency)
.normalize() .normalize()
.raw() .toBuffer(function (err, data) {
.toBuffer(function (err, data, info) { if (err) throw err;
var min = 255, max = 0, i; sharp(data).metadata(function(err, metadata) {
for (i = 0; i < data.length; i++) {
min = Math.min(min, data[i]);
max = Math.max(max, data[i]);
}
assert.strictEqual(0, min);
assert.strictEqual(255, max);
done();
});
});
it('keeps an existing alpha channel', function (done) {
sharp(fixtures.inputPngWithTransparency)
.normalize()
.toBuffer(function (err, data) {
if (err) return done(err); if (err) return done(err);
sharp(data).metadata(function(err, metadata) { assert.strictEqual(4, metadata.channels);
if (err) return done(err); assert.strictEqual(true, metadata.hasAlpha);
assert.strictEqual(4, metadata.channels); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(true, metadata.hasAlpha);
assert.strictEqual('srgb', metadata.space);
done();
});
});
});
it('keeps the alpha channel of greyscale images intact', function (done) {
sharp(fixtures.inputPngWithGreyAlpha)
.normalize()
.toBuffer(function (err, data) {
if (err) return done(err);
sharp(data).metadata(function(err, metadata) {
if (err) return done(err);
assert.strictEqual(true, metadata.hasAlpha);
assert.strictEqual(4, metadata.channels);
assert.strictEqual('srgb', metadata.space);
done();
});
});
});
it('does not alter images with only one color', function (done) {
var output = fixtures.path('output.unmodified-png-with-one-color.png');
sharp(fixtures.inputPngWithOneColor)
.normalize()
.toFile(output, function(err, info) {
if (err) done(err);
fixtures.assertMaxColourDistance(output, fixtures.inputPngWithOneColor, 0);
done(); done();
}); });
}); });
});
it('keeps the alpha channel of greyscale images intact', function(done) {
sharp(fixtures.inputPngWithGreyAlpha)
.normalize()
.toBuffer(function (err, data) {
if (err) throw err;
sharp(data).metadata(function(err, metadata) {
if (err) return done(err);
assert.strictEqual(true, metadata.hasAlpha);
assert.strictEqual(4, metadata.channels);
assert.strictEqual('srgb', metadata.space);
done();
});
});
});
it('does not alter images with only one color', function(done) {
var output = fixtures.path('output.unmodified-png-with-one-color.png');
sharp(fixtures.inputPngWithOneColor)
.normalize()
.toFile(output, function(err, info) {
if (err) done(err);
fixtures.assertMaxColourDistance(output, fixtures.inputPngWithOneColor, 0);
done();
});
});
it('works with 16-bit RGBA images', function(done) {
sharp(fixtures.inputPngWithTransparency16bit)
.normalize()
.raw()
.toBuffer(function (err, data, info) {
if (err) throw err;
assertNormalized(data);
done();
});
});
}
}); });

View File

@@ -4,8 +4,6 @@ var assert = require('assert');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
var sharp = require('../../index'); var sharp = require('../../index');
sharp.cache(0);
// Helpers // Helpers
var getPaths = function(baseName, extension) { var getPaths = function(baseName, extension) {
if (typeof extension === 'undefined') { if (typeof extension === 'undefined') {

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Resize dimensions', function() { describe('Resize dimensions', function() {
it('Exact crop', function(done) { it('Exact crop', function(done) {
@@ -130,27 +128,58 @@ describe('Resize dimensions', function() {
done(); done();
}); });
it('TIFF embed known to cause rounding errors', function(done) { if (sharp.format.tiff.input.file) {
sharp(fixtures.inputTiff).resize(240, 320).embed().jpeg().toBuffer(function(err, data, info) { it('TIFF embed known to cause rounding errors', function(done) {
if (err) throw err; sharp(fixtures.inputTiff)
assert.strictEqual(true, data.length > 0); .resize(240, 320)
assert.strictEqual('jpeg', info.format); .embed()
assert.strictEqual(240, info.width); .jpeg()
assert.strictEqual(320, info.height); .toBuffer(function(err, data, info) {
done(); if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(240, info.width);
assert.strictEqual(320, info.height);
done();
});
}); });
});
it('TIFF known to cause rounding errors', function(done) { it('TIFF known to cause rounding errors', function(done) {
sharp(fixtures.inputTiff).resize(240, 320).jpeg().toBuffer(function(err, data, info) { sharp(fixtures.inputTiff)
if (err) throw err; .resize(240, 320)
assert.strictEqual(true, data.length > 0); .jpeg()
assert.strictEqual('jpeg', info.format); .toBuffer(function(err, data, info) {
assert.strictEqual(240, info.width); if (err) throw err;
assert.strictEqual(320, info.height); assert.strictEqual(true, data.length > 0);
done(); assert.strictEqual('jpeg', info.format);
assert.strictEqual(240, info.width);
assert.strictEqual(320, info.height);
done();
});
}); });
});
it('Max width or height considering ratio (portrait)', function(done) {
sharp(fixtures.inputTiff).resize(320, 320).max().jpeg().toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(243, info.width);
assert.strictEqual(320, info.height);
done();
});
});
it('Min width or height considering ratio (portrait)', function(done) {
sharp(fixtures.inputTiff).resize(320, 320).min().jpeg().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(422, info.height);
done();
});
});
}
it('Max width or height considering ratio (landscape)', function(done) { it('Max width or height considering ratio (landscape)', function(done) {
sharp(fixtures.inputJpg).resize(320, 320).max().toBuffer(function(err, data, info) { sharp(fixtures.inputJpg).resize(320, 320).max().toBuffer(function(err, data, info) {
@@ -163,17 +192,6 @@ describe('Resize dimensions', function() {
}); });
}); });
it('Max width or height considering ratio (portrait)', function(done) {
sharp(fixtures.inputTiff).resize(320, 320).max().jpeg().toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(243, info.width);
assert.strictEqual(320, info.height);
done();
});
});
it('Provide only one dimension with max, should default to crop', function(done) { it('Provide only one dimension with max, should default to crop', function(done) {
sharp(fixtures.inputJpg).resize(320).max().toBuffer(function(err, data, info) { sharp(fixtures.inputJpg).resize(320).max().toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
@@ -196,17 +214,6 @@ describe('Resize dimensions', function() {
}); });
}); });
it('Min width or height considering ratio (portrait)', function(done) {
sharp(fixtures.inputTiff).resize(320, 320).min().jpeg().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(422, info.height);
done();
});
});
it('Provide only one dimension with min, should default to crop', function(done) { it('Provide only one dimension with min, should default to crop', function(done) {
sharp(fixtures.inputJpg).resize(320).min().toBuffer(function(err, data, info) { sharp(fixtures.inputJpg).resize(320).min().toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Rotation', function() { describe('Rotation', function() {
['Landscape', 'Portrait'].forEach(function(orientation) { ['Landscape', 'Portrait'].forEach(function(orientation) {
@@ -113,7 +111,7 @@ describe('Rotation', function() {
}); });
it('Attempt to auto-rotate image format without EXIF support', function(done) { it('Attempt to auto-rotate image format without EXIF support', function(done) {
sharp(fixtures.inputGif) sharp(fixtures.inputPng)
.rotate() .rotate()
.resize(320) .resize(320)
.jpeg() .jpeg()
@@ -122,7 +120,7 @@ describe('Rotation', function() {
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(213, info.height); assert.strictEqual(236, info.height);
done(); done();
}); });
}); });

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Sharpen', function() { describe('Sharpen', function() {
it('specific radius 10', function(done) { it('specific radius 10', function(done) {

View File

@@ -5,8 +5,6 @@ var assert = require('assert');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
describe('Threshold', function() { describe('Threshold', function() {
it('threshold 1 jpeg', function(done) { it('threshold 1 jpeg', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)

View File

@@ -10,8 +10,6 @@ var rimraf = require('rimraf');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.cache(0);
// Verifies all tiles in a given dz output directory are <= size // Verifies all tiles in a given dz output directory are <= size
var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done) { var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done) {
// Get levels // Get levels

View File

@@ -10,20 +10,48 @@ describe('Utilities', function() {
describe('Cache', function() { describe('Cache', function() {
it('Can be disabled', function() { it('Can be disabled', function() {
var cache = sharp.cache(0, 0); sharp.cache(false);
assert.strictEqual(0, cache.memory); var cache = sharp.cache(false);
assert.strictEqual(0, cache.items); assert.strictEqual(cache.memory.current, 0);
assert.strictEqual(cache.memory.max, 0);
assert.strictEqual(typeof cache.memory.high, 'number');
assert.strictEqual(cache.files.current, 0);
assert.strictEqual(cache.files.max, 0);
assert.strictEqual(cache.items.current, 0);
assert.strictEqual(cache.items.max, 0);
}); });
it('Can be set to a maximum of 50MB and 500 items', function() { it('Can be enabled with defaults', function() {
var cache = sharp.cache(50, 500); var cache = sharp.cache(true);
assert.strictEqual(50, cache.memory); assert.strictEqual(cache.memory.max, 50);
assert.strictEqual(500, cache.items); assert.strictEqual(cache.files.max, 20);
assert.strictEqual(cache.items.max, 100);
});
it('Can be set to zero', function() {
var cache = sharp.cache({
memory: 0,
files: 0,
items: 0
});
assert.strictEqual(cache.memory.max, 0);
assert.strictEqual(cache.files.max, 0);
assert.strictEqual(cache.items.max, 0);
});
it('Can be set to a maximum of 10MB, 100 files and 1000 items', function() {
var cache = sharp.cache({
memory: 10,
files: 100,
items: 1000
});
assert.strictEqual(cache.memory.max, 10);
assert.strictEqual(cache.files.max, 100);
assert.strictEqual(cache.items.max, 1000);
}); });
it('Ignores invalid values', function() { it('Ignores invalid values', function() {
sharp.cache(50, 500); sharp.cache(true);
var cache = sharp.cache('spoons'); var cache = sharp.cache('spoons');
assert.strictEqual(50, cache.memory); assert.strictEqual(cache.memory.max, 50);
assert.strictEqual(500, cache.items); assert.strictEqual(cache.files.max, 20);
assert.strictEqual(cache.items.max, 100);
}); });
}); });
@@ -85,6 +113,13 @@ describe('Utilities', function() {
}); });
}); });
}); });
it('Raw file=false, buffer=true, stream=true', function() {
['input', 'output'].forEach(function(direction) {
assert.strictEqual(false, sharp.format.raw[direction].file);
assert.strictEqual(true, sharp.format.raw[direction].buffer);
assert.strictEqual(true, sharp.format.raw[direction].stream);
});
});
}); });
describe('Versions', function() { describe('Versions', function() {