Compare commits

..

80 Commits

Author SHA1 Message Date
Lovell Fuller
20e75dc50b v0.12.2 2016-01-16 11:29:23 +00:00
Lovell Fuller
d2e5441d6e Dependency version bumps 2016-01-14 18:39:29 +00:00
Lovell Fuller
0ffa1e72d0 Attempt to remove temp file after install #331 2016-01-06 16:31:01 +00:00
Lovell Fuller
a0e034a9e9 Add support for pre-compiled libvips on ARM CPUs
Uses a HypriotOS-managed docker container for this
2016-01-06 15:50:36 +00:00
Lovell Fuller
3c7cbf8685 Update libvips to v8.2.0
Plus version bumps of other dependencies
2016-01-02 14:19:46 +00:00
Lovell Fuller
7541dfcab2 Document support for FreeBSD/gmake #326 2016-01-01 16:34:18 +00:00
Lovell Fuller
dc2b79ac9a Use array context for include/library paths in gyp config
Remove -I prefix from include paths
Should allow for compilation on FreeBSD #326
2016-01-01 14:04:52 +00:00
Lovell Fuller
6d62051877 Bump minimum version of libvips to 8.1.1 #324
Debian-based systems must compile from source
2015-12-23 20:54:37 +00:00
Lovell Fuller
61b86744d7 Ensure 16-bit input images work with embed option #325 2015-12-23 20:46:49 +00:00
Lovell Fuller
fd5b4a131f v0.12.1 2015-12-12 10:21:36 +00:00
Lovell Fuller
32c4b9eff1 Allow SIMD vector unit to be toggled on/off #172
Currently defaults to off but future versions may default to on
2015-12-12 09:11:50 +00:00
Lovell Fuller
95cf35efc5 Add changelog for v0.12.1 2015-12-06 20:40:05 +00:00
Lovell Fuller
58e6368525 Ensure embedded ICC profiles output with perceptual intent #321 2015-12-06 20:24:17 +00:00
Lovell Fuller
16e0d54b15 Use the NPM-configured HTTPS proxy, if present 2015-12-03 21:30:47 +00:00
Lovell Fuller
be381e4440 Add release date for v0.12.0 2015-11-23 14:06:49 +00:00
Lovell Fuller
9982182926 Update perf test results ahead of v0.12.0 2015-11-23 13:09:06 +00:00
Lovell Fuller
607d157b76 Benchmark test updates ahead of v0.12.0 2015-11-23 10:53:52 +00:00
Lovell Fuller
e21277ceba Version bump of libpng Windows dependency 2015-11-22 21:11:29 +00:00
Lovell Fuller
8012733a52 Expose libvips+deps versions attribute
Add versions.json for Linux packaging

Bump vips-dev Windows version for latest libpng
2015-11-22 20:58:38 +00:00
Lovell Fuller
01a1377972 Include cmath header for clang #301 2015-11-22 09:39:42 +00:00
Lovell Fuller
37e4b9b5ba Update changelog ahead of v0.12.0
Highlight features in readme+docs

Add link to Docker-based Linux CI build status
2015-11-22 09:17:51 +00:00
Lovell Fuller
8a3098604c Remove experimental code that could prevent anti-alias filter 2015-11-21 23:37:44 +00:00
Lovell Fuller
5febce7a59 Remove executable bit from test/* file permissions 2015-11-21 23:05:48 +00:00
Lovell Fuller
3dbedf1fb6 Match g_malloc/g_free for make benefit glorious nation of Windows
Remove C++ standard lib from Windows packaging
2015-11-21 22:51:32 +00:00
Lovell Fuller
0ae619dfc5 Remove stray win32 library that was causing segfaults
Explicit int cast to prevent 'loss of precision' warnings

Remove unnecessary semver checking from I/O tests
2015-11-21 22:18:39 +00:00
Lovell Fuller
05dd191e17 Ensure 16-bit+alpha input images work with vips_premultiply #301
Improves SVG support as *magick serves these as 16-bit

Add automated tests for SVG and 16-bit+alpha PNG inputs
2015-11-21 20:21:34 +00:00
Lovell Fuller
c9ecc7a517 Document Debian 7 support
Add liborc to fix openSUSE support

Switch recommended *magick to ImageMagick for OS X

Increase test timeouts (for Circle CI)
2015-11-19 22:47:34 +00:00
Lovell Fuller
434a433a09 Make output of packaging tests easier to understand
Slight simplification of Linux packaging script
2015-11-19 21:36:16 +00:00
Lovell Fuller
3de54d897c Merge pull request #309 from papandreou/feature/extractWithOptionsObject
Add an options object to the extract operation, deprecate existing behaviour.
2015-11-18 11:48:11 +00:00
Andreas Lind
530c2a9fcf Stop using the legacy .extract(top,left,width,height) in the documentation. 2015-11-18 12:06:10 +01:00
Andreas Lind
60b8b92630 Add support for .extract({left:...,top:...,width:...,height:...}). 2015-11-18 12:06:10 +01:00
Lovell Fuller
5842da22d8 Merge pull request #306 from dacarley/negate
Add negate operation to invert all pixel values.
2015-11-17 20:45:09 +00:00
Lovell Fuller
9850e3dae0 Merge pull request #303 from dacarley/threshold
Add threshold operation.
Converts to greyscale then splits into black/white based on given value.
2015-11-17 20:34:40 +00:00
David Carley
3af62446fc Implements greyscale thresholding 2015-11-17 12:15:34 -06:00
Lovell Fuller
1f71dade67 Merge pull request #307 from papandreou/wheezy
Linux Dockerfile: Use Debian Wheezy as the base image
2015-11-17 17:29:35 +00:00
Lovell Fuller
8be664b66f Merge pull request #305 from papandreou/fix/imagemagickUrl
Dockerfile: Update ImageMagick tarball url (they took -5 down and up -6)
2015-11-17 17:24:30 +00:00
Andreas Lind
c0be4f1307 Dial back the required libc version to 2.13 2015-11-17 17:37:06 +01:00
Andreas Lind
d9c754f5c1 Linux Dockerfile: Use a Debian Wheezy image instead of Ubuntu Precise. 2015-11-17 17:37:06 +01:00
David Carley
33a175eafb Implements negation. 2015-11-17 10:18:59 -06:00
Andreas Lind
7c990b3ab3 Dockerfile: Update ImageMagick tarball url (they took -5 down and up put -6). 2015-11-17 17:16:19 +01:00
Lovell Fuller
5dfeaa9fd1 Ensure gaussian blur is applied before lbb interpolator #289 2015-11-16 08:26:35 +00:00
Lovell Fuller
84fd1caa46 Switch default interpolator to bicubic #289
Only use gaussian blur for non-linear interpolators

Improves performance of bilinear by ~15%

Add liborc to the packaged build to improve bicubic perf

Add examples of the various interpolation methods

Add bilinear vs bicubic to perf tests
2015-11-15 22:04:31 +00:00
Lovell Fuller
2678d761ba Use FreeCallback to support mixed Windows runtime libs #152 2015-11-14 13:58:05 +00:00
Lovell Fuller
ede2ee9ce3 Use Persistent wrapper to prevent GC of input Buffer #152
Avoids memcpy of input and output Buffer objects

Improves Buffer and Stream performance by ~3%
2015-11-14 11:24:15 +00:00
Lovell Fuller
20f468991f Start to use libvips 8.1.0+ features #152
Use native (un)premultiply

Support normalise on Windows
2015-11-12 22:14:53 +00:00
Lovell Fuller
58d9e0fef7 Pre-extract rotate should not swap width/height #296 2015-11-11 22:34:30 +00:00
Lovell Fuller
d7278f022b Ensure latest npm on openSUSE test 2015-11-11 22:25:17 +00:00
Lovell Fuller
7383596f8c Allow tty-less docker-inside-docker usage 2015-11-11 21:37:04 +00:00
Lovell Fuller
f6831ab46b Increase packaging test logging 2015-11-10 23:30:08 +00:00
Lovell Fuller
7cf0f95ed1 Fix Circle CI config
CI providers should really provide config linters :)
2015-11-10 21:53:26 +00:00
Lovell Fuller
bf6b894480 Improve dependency-less documentation
Start to comment ever-growing GYP config

Add Circle CI config to run packaging tests
2015-11-10 21:49:45 +00:00
Lovell Fuller
ee8fcb6109 Update docs to reflect ease-of-installation 2015-11-10 00:04:06 +00:00
Lovell Fuller
05cec013fe Ensure support for more Linux flavours
Add docker-based packaging tests
2015-11-09 08:37:30 +00:00
Lovell Fuller
f4cbbd7b79 Ensure Windows CI uses x64 2015-11-07 20:22:17 +00:00
Lovell Fuller
2129adfcc3 Initial commit of local libvips binding/packaging
Copy Windows DLLs into release dir as no rpath equivalent
Use local libvips on Windows CI
2015-11-07 19:58:26 +00:00
Lovell Fuller
9f59a2aebf Version bumps and changelog for v0.11.4 2015-11-05 21:36:44 +00:00
Lovell Fuller
26fb75bf3f Merge pull request #291 from brandonaaron/corner-gravity
Add corner gravity support
2015-10-27 21:17:53 +00:00
Brandon Aaron
25e5f27785 add corner gavity support 2015-10-27 15:10:10 -04:00
Lovell Fuller
ef62daccf9 Upgrade CI and preinstall script to libvips 8.1.1 2015-10-16 23:43:55 +01:00
Lovell Fuller
9067c0a000 Merge pull request #288 from brandonaaron/exif_flop_2_and_4
EXIF Orientation tags 2 and 4 were flipping instead of flopping
2015-10-16 21:45:16 +01:00
Brandon Aaron
79470d2e07 EXIF Orientation tags 2 and 4 were flipping instead of flopping 2015-10-16 15:41:40 -04:00
Lovell Fuller
3cefa6f2bf Merge pull request #287 from vlapo/master
Add --runtime_link=static option to GYP binding for use with AWS Lambda
2015-10-14 19:22:09 +01:00
vlapo
75d954a6bc Add --runtime_link=static 2015-10-14 11:29:29 +02:00
Lovell Fuller
1b7e3746cc Merge pull request #286 from rayshan/patch-1
Replace `filename` with `path` to make consistent with Node's `fs` module
2015-10-13 21:03:32 +01:00
Ray Shan
29252d9dbb Clarify fileName argument by changing to path
I noticed we can do something like `.toFile("tmp/temp.jpg")`
2015-10-13 12:21:41 -07:00
Lovell Fuller
23e14861be Update perf test results to include jimp
Remove imagemagick-native until Node v4 support
2015-10-10 18:41:01 +01:00
Lovell Fuller
97960b5f91 Merge branch 'master' of https://github.com/lovell/sharp 2015-10-10 13:44:42 +01:00
Lovell Fuller
18c4ad9adf Version bumps of perf test candidates 2015-10-10 13:44:02 +01:00
Lovell Fuller
b240c53633 Merge pull request #282 from jabbrass/patch-1
Fix typo (docs/api)
2015-10-08 07:27:35 +01:00
J. Andrew Brassington
660f3d58be Fix typo (docs/api)
Line 330: "betweem" => "between"
2015-10-07 19:01:35 -07:00
Lovell Fuller
b6d75cda8e Revert Windows/Appveyor CI to libvips 8.0.2 2015-10-07 21:28:58 +01:00
Lovell Fuller
e07356c11c Update list of preinstall OS support
Upgrade to libvips 8.1.0
2015-10-07 20:49:47 +01:00
Lovell Fuller
82e215a42e Merge pull request #280 from roryrjb/master
(preinstall) add wily to 'supported' distros
2015-10-03 10:20:51 +01:00
Rory Bradford
cc1c36d891 (preinstall) add wily to 'supported' distros 2015-10-03 09:45:46 +01:00
Lovell Fuller
a1a2d7de5c Centos 7 provides LittleCMS via lcms2-devel #278 2015-09-30 17:35:35 +01:00
Lovell Fuller
6dce2deb82 Add jimp to perf tests #275 2015-09-27 09:38:47 +01:00
Lovell Fuller
cdad84edc6 Merge pull request #266 from roryrjb/master
(preinstall) add freya to 'supported' distros
2015-09-11 17:32:03 +01:00
Rory Bradford
de842a67d8 (preinstall) add freya to 'supported' distros 2015-09-11 17:10:25 +01:00
Lovell Fuller
918bbe88c6 Switch Travis to Ubuntu 14.04/GCE
Remove v0.10 and custom msvs flag from Appveyor
2015-09-09 22:18:22 +01:00
Lovell Fuller
7d3891989c Add Node.js v4 to CI builds 2015-09-08 20:50:33 +01:00
128 changed files with 3089 additions and 1183 deletions

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

6
.gitignore vendored
View File

@@ -4,6 +4,8 @@ coverage
test/bench/node_modules test/bench/node_modules
test/fixtures/output* test/fixtures/output*
test/leak/libvips.supp test/leak/libvips.supp
lib
# Mac OS X include
packaging/libvips*
packaging/*.log
.DS_Store .DS_Store

View File

@@ -1,10 +1,15 @@
build build
node_modules node_modules
coverage coverage
.editorconfig
.jshintignore .jshintignore
.jshintrc .jshintrc
.gitignore .gitignore
test test
.travis.yml .travis.yml
appveyor.yml appveyor.yml
circle.yml
mkdocs.yml mkdocs.yml
lib
include
packaging

View File

@@ -2,16 +2,16 @@ language: node_js
node_js: node_js:
- "0.10" - "0.10"
- "0.12" - "0.12"
- "iojs-v2" - "4"
- "iojs-v3" - "5"
sudo: false
addons: addons:
apt: apt:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
packages: packages:
- g++-4.8 - g++-4.8
before_install: env:
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90 CXX=g++-4.8
- curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
after_success: after_success:
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

View File

@@ -6,16 +6,10 @@ Hello, thank you for your interest in helping!
Please create a [new issue](https://github.com/lovell/sharp/issues/new) containing the steps to reproduce the problem. Please create a [new issue](https://github.com/lovell/sharp/issues/new) containing the steps to reproduce the problem.
If you're having installation problems, please include the output of running `npm install --verbose sharp`.
New bugs are assigned a `triage` label whilst under investigation. New bugs are assigned a `triage` label whilst under investigation.
If you're having problems with `npm install sharp`, please include the output of the following commands, perhaps as a [gist](https://gist.github.com/):
```sh
vips -v
pkg-config --print-provides vips
npm install --verbose sharp
```
## Submit a new feature request ## Submit a new feature request
If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists, it's probably fastest to add a comment to it about your requirement. If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists, it's probably fastest to add a comment to it about your requirement.
@@ -47,7 +41,6 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch | | Release | WIP branch |
| ------: | :--------- | | ------: | :--------- |
| v0.11.0 | knife |
| v0.12.0 | look | | v0.12.0 | look |
| v0.13.0 | mind | | v0.13.0 | mind |
@@ -85,6 +78,15 @@ Requires [Valgrind](http://valgrind.org/).
npm run test-leak npm run test-leak
``` ```
### Packaging tests
Tests the installation on a number of Linux-based operating systems.
Requires docker.
```sh
npm run test-packaging
```
## Finally ## Finally
Please feel free to ask any questions via a [new issue](https://github.com/lovell/sharp/issues/new) or contact me by [e-mail](https://github.com/lovell/sharp/blob/master/package.json#L4). Please feel free to ask any questions via a [new issue](https://github.com/lovell/sharp/issues/new) or contact me by [e-mail](https://github.com/lovell/sharp/blob/master/package.json#L4).

View File

@@ -4,13 +4,30 @@ 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 the
quickest ImageMagick and GraphicsMagick settings.
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.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most Windows (x64), Linux and ARMv6+ systems do not require
the installation of any external runtime dependencies.
Use with OS X is as simple as running `brew install homebrew/science/vips`
to install the libvips dependency.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master) [![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
### Documentation ### Documentation
Visit [sharp.dimens.io](http://sharp.dimens.io/) for Visit [sharp.dimens.io](http://sharp.dimens.io/) for complete
complete installation instructions, API documentation, [installation instructions](http://sharp.dimens.io/page/install),
benchmark tests and a changelog. [API documentation](http://sharp.dimens.io/page/api),
[benchmark tests](http://sharp.dimens.io/page/performance) and
[changelog](http://sharp.dimens.io/page/changelog).
### Contributing ### Contributing
@@ -19,7 +36,7 @@ covers reporting bugs, requesting features and submitting code changes.
### Licence ### Licence
Copyright 2013, 2014, 2015 Lovell Fuller and contributors. Copyright 2013, 2014, 2015, 2016 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -3,26 +3,13 @@ version: "{build}"
build: off build: off
platform: x64 platform: x64
environment: environment:
VIPS_VERSION_MAJOR_MINOR: 8.0
VIPS_VERSION_PATCH: 2
VIPS_WARNING: 0 VIPS_WARNING: 0
matrix: matrix:
- nodejs_version: "0.10"
nodejs_exec: "node"
- nodejs_version: "0.12" - nodejs_version: "0.12"
nodejs_exec: "node" - nodejs_version: "4"
- nodejs_version: "2" - nodejs_version: "5"
nodejs_exec: "iojs"
- nodejs_version: "3"
nodejs_exec: "iojs"
install: install:
- ps: $env:VIPS_VERSION = "$env:VIPS_VERSION_MAJOR_MINOR.$env:VIPS_VERSION_PATCH" - ps: Install-Product node $env:nodejs_version x64
- ps: Write-Output "Fetching http://www.vips.ecs.soton.ac.uk/supported/$env:VIPS_VERSION_MAJOR_MINOR/win32/vips-dev-$env:VIPS_VERSION.zip" - npm install
- ps: Start-FileDownload http://www.vips.ecs.soton.ac.uk/supported/$env:VIPS_VERSION_MAJOR_MINOR/win32/vips-dev-$env:VIPS_VERSION.zip -FileName c:\vips-dev-$env:VIPS_VERSION.zip
- ps: Invoke-Expression "& 7z -y x c:\vips-dev-$env:VIPS_VERSION.zip -oc:\ | FIND /V `"ing `""
- ps: $env:VIPS_HOME = "c:\vips-dev-$env:VIPS_VERSION"
- ps: $env:PATH = "$env:VIPS_HOME\bin;$env:PATH"
- ps: Install-Product node $env:nodejs_version x86
- npm install --arch=ia32 --msvs_version=2013
test_script: test_script:
- npm run-script test-win32-%nodejs_exec% - npm run-script test-win

201
binding.gyp Executable file → Normal file
View File

@@ -1,6 +1,40 @@
{ {
'targets': [{ 'targets': [{
'target_name': 'sharp', 'target_name': 'sharp',
# Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
'variables': {
'variables': {
'variables': {
'conditions': [
['OS != "win"', {
# Build the PKG_CONFIG_PATH environment variable with all possible combinations
'pkg_config_path': '<!(which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR || true):$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig'
}, {
'pkg_config_path': ''
}]
],
},
'conditions': [
['OS != "win"', {
# 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_path%': '<(pkg_config_path)',
'runtime_link%': 'shared',
'conditions': [
['OS != "win"', {
# Does the globally available version of libvips, if any, meet the minimum version requirement?
'use_global_vips': '<!(GLOBAL_VIPS_VERSION="<(global_vips_version)" node -e "require(\'./binding\').use_global_vips()")'
}, {
'use_global_vips': ''
}]
]
},
'sources': [ 'sources': [
'src/common.cc', 'src/common.cc',
'src/metadata.cc', 'src/metadata.cc',
@@ -9,50 +43,70 @@
'src/sharp.cc', 'src/sharp.cc',
'src/utilities.cc' 'src/utilities.cc'
], ],
'include_dirs': [
'<!(node -e "require(\'nan\')")'
],
'conditions': [ 'conditions': [
['OS=="win"', { ['use_global_vips == "true"', {
'library_dirs': [ # Use pkg-config for include and lib
'$(VIPS_HOME)/lib' 'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips glib-2.0 | sed s\/-I//g)'],
], 'conditions': [
'libraries': [ ['runtime_link == "static"', {
'libvips.dll.a', 'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips)']
'glib-2.0.lib', }, {
'gobject-2.0.lib', 'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips)']
'gthread-2.0.lib', }]
'gmodule-2.0.lib', ]
'liblcms2.dll.a', }, {
'libxml2.lib', # Attempt to download pre-built libvips and install locally within node_modules
'intl.lib', 'include_dirs': [
'libjpeg.dll.a', '<(module_root_dir)/include',
'libexif.dll.a', '<(module_root_dir)/include/glib-2.0',
'libpng.lib', '<(module_root_dir)/lib/glib-2.0/include'
'libtiff.dll.a', ],
'libMagickWand-6.Q16.dll.a', 'conditions': [
'libMagickCore-6.Q16.dll.a', ['OS == "win"', {
'pango-1.0.lib',
'pangoft2-1.0.lib',
'libgsf-1.dll.a',
'libopenslide.dll.a',
'libfftw3.dll.a'
],
'include_dirs': [
'$(VIPS_HOME)/include',
'$(VIPS_HOME)/include/glib-2.0',
'$(VIPS_HOME)/lib/glib-2.0/include',
'<!(node -e "require(\'nan\')")'
]
}, {
'variables': { 'variables': {
'PKG_CONFIG_PATH': '<!(which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR || true):$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig' 'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
}, },
'libraries': [ 'libraries': [
'<!(PKG_CONFIG_PATH="<(PKG_CONFIG_PATH)" pkg-config --libs vips)' '<(module_root_dir)/lib/libvips.lib',
], '<(module_root_dir)/lib/libglib-2.0.lib',
'include_dirs': [ '<(module_root_dir)/lib/libgobject-2.0.lib'
'<!(PKG_CONFIG_PATH="<(PKG_CONFIG_PATH)" pkg-config --cflags vips glib-2.0)',
'<!(node -e "require(\'nan\')")'
] ]
}] }],
['OS == "linux"', {
'variables': {
'download_vips': '<!(LDD_VERSION="<!(ldd --version 2>&1 || true)" node -e "require(\'./binding\').download_vips()")'
},
'libraries': [
'<(module_root_dir)/lib/libvips.so',
'<(module_root_dir)/lib/libglib-2.0.so',
'<(module_root_dir)/lib/libgobject-2.0.so',
# Dependencies of dependencies, included for openSUSE support
'<(module_root_dir)/lib/libMagickCore-6.Q16.so',
'<(module_root_dir)/lib/libMagickWand-6.Q16.so',
'<(module_root_dir)/lib/libexif.so',
'<(module_root_dir)/lib/libgio-2.0.so',
'<(module_root_dir)/lib/libgmodule-2.0.so',
'<(module_root_dir)/lib/libgsf-1.so',
'<(module_root_dir)/lib/libjpeg.so',
'<(module_root_dir)/lib/libpng.so',
'<(module_root_dir)/lib/libtiff.so',
'<(module_root_dir)/lib/libwebp.so',
'<(module_root_dir)/lib/libz.so',
'<(module_root_dir)/lib/libffi.so',
'<(module_root_dir)/lib/libgthread-2.0.so',
'<(module_root_dir)/lib/liblcms2.so',
'<(module_root_dir)/lib/libpng16.so',
'<(module_root_dir)/lib/libxml2.so',
'<(module_root_dir)/lib/liborc-0.4.so',
# Ensure runtime linking is relative to sharp.node
'-Wl,-rpath=\'$${ORIGIN}/../../lib\''
]
}]
]
}]
], ],
'cflags_cc': [ 'cflags_cc': [
'-std=c++0x', '-std=c++0x',
@@ -61,28 +115,77 @@
'-O3' '-O3'
], ],
'xcode_settings': { 'xcode_settings': {
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.7',
'OTHER_CPLUSPLUSFLAGS': [ 'OTHER_CPLUSPLUSFLAGS': [
'-std=c++11',
'-stdlib=libc++',
'-fexceptions', '-fexceptions',
'-Wall', '-Wall',
'-O3' '-O3'
], ]
'MACOSX_DEPLOYMENT_TARGET': '10.7'
},
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1 # /EHsc
}
}, },
'configurations': { 'configurations': {
'Release': { 'Release': {
'msvs_settings': { 'msvs_settings': {
'VCCLCompilerTool': { 'VCCLCompilerTool': {
'ExceptionHandling': 1, 'ExceptionHandling': 1
} }
} }
} }
}, },
}, {
'target_name': 'win_copy_dlls',
'type': 'none',
'dependencies': [
'sharp'
],
'conditions': [
['OS == "win"', {
# Windows lacks support for rpath
'copies': [{
'destination': '<(module_root_dir)/build/Release',
'files': [
'<(module_root_dir)/lib/GNU.Gettext.dll',
'<(module_root_dir)/lib/libMagickCore-6.Q16-2.dll',
'<(module_root_dir)/lib/libMagickWand-6.Q16-2.dll',
'<(module_root_dir)/lib/libasprintf-0.dll',
'<(module_root_dir)/lib/libcairo-2.dll',
'<(module_root_dir)/lib/libcairo-gobject-2.dll',
'<(module_root_dir)/lib/libcairo-script-interpreter-2.dll',
'<(module_root_dir)/lib/libexif-12.dll',
'<(module_root_dir)/lib/libexpat-1.dll',
'<(module_root_dir)/lib/libffi-6.dll',
'<(module_root_dir)/lib/libfftw3-3.dll',
'<(module_root_dir)/lib/libfontconfig-1.dll',
'<(module_root_dir)/lib/libfreetype-6.dll',
'<(module_root_dir)/lib/libgcc_s_seh-1.dll',
'<(module_root_dir)/lib/libgdk_pixbuf-2.0-0.dll',
'<(module_root_dir)/lib/libgio-2.0-0.dll',
'<(module_root_dir)/lib/libglib-2.0-0.dll',
'<(module_root_dir)/lib/libgmodule-2.0-0.dll',
'<(module_root_dir)/lib/libgobject-2.0-0.dll',
'<(module_root_dir)/lib/libgsf-1-114.dll',
'<(module_root_dir)/lib/libgthread-2.0-0.dll',
'<(module_root_dir)/lib/libintl-8.dll',
'<(module_root_dir)/lib/libjpeg-62.dll',
'<(module_root_dir)/lib/liblcms2-2.dll',
'<(module_root_dir)/lib/libopenjpeg-1.dll',
'<(module_root_dir)/lib/libopenslide-0.dll',
'<(module_root_dir)/lib/libpango-1.0-0.dll',
'<(module_root_dir)/lib/libpangocairo-1.0-0.dll',
'<(module_root_dir)/lib/libpangowin32-1.0-0.dll',
'<(module_root_dir)/lib/libpixman-1-0.dll',
'<(module_root_dir)/lib/libpng16-16.dll',
'<(module_root_dir)/lib/libquadmath-0.dll',
'<(module_root_dir)/lib/libsqlite3-0.dll',
'<(module_root_dir)/lib/libssp-0.dll',
'<(module_root_dir)/lib/libtiff-5.dll',
'<(module_root_dir)/lib/libvips-42.dll',
'<(module_root_dir)/lib/libxml2-2.dll',
'<(module_root_dir)/lib/zlib1.dll'
]
}]
}]
]
}] }]
} }

127
binding.js Normal file
View File

@@ -0,0 +1,127 @@
'use strict';
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');
var semver = require('semver');
var request = require('request');
var tar = require('tar');
var tmp = require('os').tmpdir();
var distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
var vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h');
// -- Helpers
// Does this file exist?
var isFile = function(file) {
var exists = false;
try {
exists = fs.statSync(file).isFile();
} catch (err) {}
return exists;
};
var unpack = function(tarPath, done) {
var extractor = tar.Extract({
path: __dirname
});
extractor.on('error', error);
extractor.on('end', function() {
if (!isFile(vipsHeaderPath)) {
error('Could not unpack ' + tarPath);
}
if (typeof done === 'function') {
done();
}
});
fs.createReadStream(tarPath).on('error', error)
.pipe(zlib.Unzip())
.pipe(extractor);
};
// Error
var error = function(msg) {
if (msg instanceof Error) {
msg = msg.message;
}
process.stderr.write('ERROR: ' + msg + '\n');
process.exit(1);
};
// -- Binary downloaders
module.exports.download_vips = function() {
// Has vips been installed locally?
if (!isFile(vipsHeaderPath)) {
// Ensure Intel 64-bit or ARM
if (process.arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
// Ensure libc >= 2.15
var lddVersion = process.env.LDD_VERSION;
if (lddVersion) {
var libcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
if (libcVersion && semver.lt(libcVersion + '.0', '2.13.0')) {
error('libc version ' + libcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
}
// Arch/platform-specific .tar.gz
var platform = (process.arch === 'arm') ? 'arm' : process.platform.substr(0, 3);
var tarFilename = ['libvips', process.env.npm_package_config_libvips, platform].join('-') + '.tar.gz';
var tarPath = path.join(__dirname, 'packaging', tarFilename);
if (isFile(tarPath)) {
unpack(tarPath);
} else {
// Download to per-process temporary file
tarPath = path.join(tmp, process.pid + '-' + tarFilename);
var tmpFile = fs.createWriteStream(tarPath).on('finish', function() {
unpack(tarPath, function() {
// Attempt to remove temporary file
try {
fs.unlinkSync(tarPath);
} catch (err) {}
});
});
var options = {
url: distBaseUrl + tarFilename
};
if (process.env.npm_config_https_proxy) {
// Use the NPM-configured HTTPS proxy
options.proxy = process.env.npm_config_https_proxy;
}
request(options).on('response', function(response) {
if (response.statusCode !== 200) {
error(distBaseUrl + tarFilename + ' status code ' + response.statusCode);
}
}).on('error', function(err) {
error('Download from ' + distBaseUrl + tarFilename + ' failed: ' + err.message);
}).pipe(tmpFile);
}
}
};
module.exports.use_global_vips = function() {
var useGlobalVips = false;
var globalVipsVersion = process.env.GLOBAL_VIPS_VERSION;
if (globalVipsVersion) {
useGlobalVips = semver.gte(
globalVipsVersion,
process.env.npm_package_config_libvips
);
}
if (process.platform === 'darwin' && !useGlobalVips) {
if (globalVipsVersion) {
error(
'Found libvips ' + globalVipsVersion + ' but require ' + process.env.npm_package_config_libvips +
'\nPlease upgrade libvips by running: brew update && brew upgrade'
);
} else {
error('Please install libvips by running: brew install homebrew/science/vips --with-webp --with-graphicsmagick');
}
}
process.stdout.write(useGlobalVips ? 'true' : 'false');
};

6
circle.yml Normal file
View File

@@ -0,0 +1,6 @@
machine:
services:
- docker
test:
override:
- ./packaging/test.sh

View File

@@ -11,7 +11,7 @@ var sharp = require('sharp');
Constructor to which further methods are chained. `input`, if present, can be one of: Constructor to which further methods are chained. `input`, if present, can be one of:
* Buffer containing JPEG, PNG, WebP, GIF* or TIFF image data, or * Buffer containing JPEG, PNG, WebP, GIF* or TIFF image data, or
* String containing the filename of an image, with most major formats supported. * String containing the path to an image file, with most major formats supported.
The object returned implements the The object returned 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.
@@ -78,7 +78,7 @@ to share a single input Stream.
```javascript ```javascript
var pipeline = sharp().rotate(); var pipeline = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(firstWritableStream); pipeline.clone().resize(800, 600).pipe(firstWritableStream);
pipeline.clone().extract(20, 20, 100, 100).pipe(secondWritableStream); pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
readableStream.pipe(pipeline); readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream // firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream // secondWritableStream receives auto-rotated, extracted region of readableStream
@@ -111,7 +111,8 @@ Crop the resized image to the exact size specified, the default behaviour.
`gravity`, if present, is a String or an attribute of the `sharp.gravity` Object e.g. `sharp.gravity.north`. `gravity`, if present, is a String or an attribute of the `sharp.gravity` Object e.g. `sharp.gravity.north`.
Possible values are `north`, `east`, `south`, `west`, `center` and `centre`. The default gravity is `center`/`centre`. Possible values are `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` and `centre`.
The default gravity is `center`/`centre`.
```javascript ```javascript
var transformer = sharp() var transformer = sharp()
@@ -200,14 +201,18 @@ Ignoring the aspect ratio of the input, stretch the image to the exact `width` a
Use the given interpolator for image resizing, where `interpolator` is an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.bicubic`. Use the given interpolator for image resizing, where `interpolator` is an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.bicubic`.
The default interpolator is `bicubic`, providing a general-purpose interpolator that is both fast and of good quality.
Possible interpolators, in order of performance, are: Possible interpolators, in order of performance, are:
* `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation), suitable for image enlargement only. * `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation), suitable for image enlargement only.
* `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), the default and fastest image reduction interpolation. * `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), faster than bicubic but with less smooth results.
* `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation), which typically reduces performance by 5%. * `vertexSplitQuadraticBasisSpline`: Use the smoother [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48) to prevent "staircasing" when enlarging.
* `vertexSplitQuadraticBasisSpline`: Use [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48), which prevents "staircasing" and typically reduces performance by 5%. * `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default).
* `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" and typically reduces performance by a factor of 2. * `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2.
* `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance and typically reduces performance by a factor of 3. * `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance but typically reduces performance by a factor of 3.
[Compare the output of these interpolators](https://github.com/lovell/sharp/tree/master/test/interpolators)
```javascript ```javascript
sharp(inputBuffer) sharp(inputBuffer)
@@ -225,11 +230,11 @@ sharp(inputBuffer)
### Operations ### Operations
#### extract(top, left, width, height) #### extract({ left: left, top: top, width: width, height: height })
Extract a region of the image. Can be used with or without a `resize` operation. Extract a region of the image. Can be used with or without a `resize` operation.
`top` and `left` are the offset, in pixels, from the top-left corner. `left` and `top` are the offset, in pixels, from the top-left corner.
`width` and `height` are the dimensions of the extracted image. `width` and `height` are the dimensions of the extracted image.
@@ -237,7 +242,7 @@ Use `extract` before `resize` for pre-resize extraction. Use `extract` after `re
```javascript ```javascript
sharp(input) sharp(input)
.extract(top, left, width, height) .extract({ left: left, top: top, width: width, height: height })
.toFile(output, function(err) { .toFile(output, function(err) {
// Extract a region of the input image, saving in the same format. // Extract a region of the input image, saving in the same format.
}); });
@@ -245,9 +250,9 @@ sharp(input)
```javascript ```javascript
sharp(input) sharp(input)
.extract(topOffsetPre, leftOffsetPre, widthPre, heightPre) .extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
.resize(width, height) .resize(width, height)
.extract(topOffsetPost, leftOffsetPost, widthPost, heightPost) .extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
.toFile(output, function(err) { .toFile(output, function(err) {
// Extract a region, resize, then extract from the resized image // Extract a region, resize, then extract from the resized image
}); });
@@ -267,6 +272,10 @@ The default background is `{r: 0, g: 0, b: 0, a: 1}`, black without transparency
Merge alpha transparency channel, if any, with `background`. Merge alpha transparency channel, if any, with `background`.
#### negate()
Produces the "negative" of the image. White => Black, Black => White, Blue => Yellow, etc.
#### rotate([angle]) #### rotate([angle])
Rotate the output image by either an explicit angle or auto-orient based on the EXIF `Orientation` tag. Rotate the output image by either an explicit angle or auto-orient based on the EXIF `Orientation` tag.
@@ -323,11 +332,17 @@ When a `radius` is provided, performs a slower, more accurate sharpen of the L c
* `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0. * `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0.
* `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0. * `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0.
#### threshold([threshold])
Converts all pixels in the image to greyscale white or black. Any pixel greather-than-or-equal-to the threshold (0..255) will be white. All others will be black.
* `threshold`, if present, is a Number, representing the level above which pixels will be forced to white.
#### gamma([gamma]) #### gamma([gamma])
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma` then increasing the encoding (brighten) post-resize at a factor of `gamma`. Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma` then increasing the encoding (brighten) post-resize at a factor of `gamma`.
`gamma`, if present, is a Number betweem 1 and 3. The default value is `2.2`, a suitable approximation for sRGB images. `gamma`, if present, is a Number between 1 and 3. The default value is `2.2`, a suitable approximation for sRGB images.
This can improve the perceived brightness of a resized image in non-linear colour spaces. This can improve the perceived brightness of a resized image in non-linear colour spaces.
@@ -345,13 +360,13 @@ The output image will still be web-friendly sRGB and contain three (identical) c
Enhance output image contrast by stretching its luminance to cover the full dynamic range. This typically reduces performance by 30%. Enhance output image contrast by stretching its luminance to cover the full dynamic range. This typically reduces performance by 30%.
#### overlayWith(filename) #### overlayWith(path)
_Experimental_ _Experimental_
Alpha composite `filename` over the processed (resized, extracted) image. The dimensions of the two images must match. Alpha composite image at `path` over the processed (resized, extracted) image. The dimensions of the two images must match.
* `filename` is a String containing the filename of an image with an alpha channel. * `path` is a String containing the path to an image file with an alpha channel.
```javascript ```javascript
sharp('input.png') sharp('input.png')
@@ -374,9 +389,9 @@ sharp('input.png')
### Output ### Output
#### toFile(filename, [callback]) #### toFile(path, [callback])
`filename` is a String containing the filename 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. The format is 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:
@@ -411,8 +426,6 @@ Use WebP format for the output image.
#### raw() #### raw()
_Requires libvips 7.42.0+_
Provide raw, uncompressed uint8 (unsigned char) image data for Buffer and Stream based output. Provide raw, uncompressed uint8 (unsigned char) image data for Buffer and Stream based output.
The number of channels depends on the input image and selected options. The number of channels depends on the input image and selected options.
@@ -482,13 +495,11 @@ An advanced setting for the _zlib_ compression level of the lossless PNG output
#### withoutAdaptiveFiltering() #### withoutAdaptiveFiltering()
_Requires libvips 7.42.0+_
An advanced setting to disable adaptive row filtering for the lossless PNG output format. An advanced setting to disable adaptive row filtering for the lossless PNG output format.
#### trellisQuantisation() / trellisQuantization() #### trellisQuantisation() / trellisQuantization()
_Requires libvips 8.0.0+ compiled against mozjpeg 3.0+_ _Requires libvips to have been compiled with mozjpeg support_
An advanced setting to apply the use of An advanced setting to apply the use of
[trellis quantisation](http://en.wikipedia.org/wiki/Trellis_quantization) with JPEG output. [trellis quantisation](http://en.wikipedia.org/wiki/Trellis_quantization) with JPEG output.
@@ -496,7 +507,7 @@ Reduces file size and slightly increases relative quality at the cost of increas
#### overshootDeringing() #### overshootDeringing()
_Requires libvips 8.0.0+ compiled against mozjpeg 3.0+_ _Requires libvips to have been compiled with mozjpeg support_
An advanced setting to reduce the effects of An advanced setting to reduce the effects of
[ringing](http://en.wikipedia.org/wiki/Ringing_%28signal%29) in JPEG output, [ringing](http://en.wikipedia.org/wiki/Ringing_%28signal%29) in JPEG output,
@@ -504,7 +515,7 @@ in particular where black text appears on a white background (or vice versa).
#### optimiseScans() / optimizeScans() #### optimiseScans() / optimizeScans()
_Requires libvips 8.0.0+ compiled against mozjpeg 3.0+_ _Requires libvips to have been compiled with mozjpeg support_
An advanced setting for progressive (interlace) JPEG output. An advanced setting for progressive (interlace) JPEG output.
Calculates which spectrum of DCT coefficients uses the fewest bits. Calculates which spectrum of DCT coefficients uses the fewest bits.
@@ -554,6 +565,29 @@ sharp.queue.on('change', function(queueLength) {
}); });
``` ```
#### versions
An Object containing the version numbers of libvips and, on Linux, its dependencies.
```javascript
> console.log(sharp.versions);
{ zlib: '1.2.8',
ffi: '3.2.1',
glib: '2.46.2',
xml: '2.9.2',
gsf: '1.14.34',
exif: '0.6.21',
jpeg: '1.4.2',
png: '1.6.19',
lcms: '2.7',
webp: '0.4.4',
tiff: '4.0.6',
magick: '6.9.2-6',
orc: '0.4.24',
vips: '8.1.1' }
```
### Utilities ### Utilities
#### sharp.cache([memory], [items]) #### sharp.cache([memory], [items])
@@ -595,3 +629,31 @@ Provides access to internal task counters.
```javascript ```javascript
var counters = sharp.counters(); // { queue: 2, process: 4 } var counters = sharp.counters(); // { queue: 2, process: 4 }
``` ```
#### sharp.simd([enable])
_Requires libvips to have been compiled with liborc support_
Improves the performance of `resize`, `blur` and `sharpen` operations
by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
* `enable`, if present, is a boolean where `true` enables and `false` disables the use of SIMD.
This method always returns the current state.
This feature is currently disabled by default
but future versions may enable it by default.
When enabled, versions of liborc prior to 0.4.24
and versions of libvips prior to 8.2.0
have been known to crash under heavy load.
```javascript
var simd = sharp.simd();
// simd is `true` if SIMD is currently enabled
```
```javascript
var simd = sharp.simd(true);
// attempts to enable the use of SIMD, returning true if available
```

View File

@@ -1,7 +1,90 @@
# Changelog # Changelog
### v0.12 - "*look*"
#### v0.12.2 - 16<sup>th</sup> January 2016
* Upgrade libvips to v8.2.0 for improved vips_shrink.
* Add pre-compiled libvips for ARMv6+ CPUs.
* Ensure 16-bit input images work with embed option.
[#325](https://github.com/lovell/sharp/issues/325)
[@janaz](https://github.com/janaz)
* Allow compilation with gmake to provide FreeBSD support.
[#326](https://github.com/lovell/sharp/issues/326)
[@c0decafe](https://github.com/c0decafe)
* Attempt to remove temporary file after installation.
[#331](https://github.com/lovell/sharp/issues/331)
[@dtoubelis](https://github.com/dtoubelis)
#### v0.12.1 - 12<sup>th</sup> December 2015
* Allow use of SIMD vector instructions (via liborc) to be toggled on/off.
[#172](https://github.com/lovell/sharp/issues/172)
[@bkw](https://github.com/bkw)
[@puzrin](https://github.com/puzrin)
* Ensure embedded ICC profiles output with perceptual intent.
[#321](https://github.com/lovell/sharp/issues/321)
[@vlapo](https://github.com/vlapo)
* Use the NPM-configured HTTPS proxy, if any, for binary downloads.
#### v0.12.0 - 23<sup>rd</sup> November 2015
* Bundle pre-compiled libvips and its dependencies for 64-bit Linux and Windows.
[#42](https://github.com/lovell/sharp/issues/42)
* Take advantage of libvips v8.1.0+ features.
[#152](https://github.com/lovell/sharp/issues/152)
* Add support for 64-bit Windows. Drop support for 32-bit Windows.
[#224](https://github.com/lovell/sharp/issues/224)
[@sabrehagen](https://github.com/sabrehagen)
* Switch default interpolator to bicubic.
[#289](https://github.com/lovell/sharp/issues/289)
[@mahnunchik](https://github.com/mahnunchik)
* Pre-extract rotatation should not swap width/height.
[#296](https://github.com/lovell/sharp/issues/296)
[@asilvas](https://github.com/asilvas)
* Ensure 16-bit+alpha input images are (un)premultiplied correctly.
[#301](https://github.com/lovell/sharp/issues/301)
[@izaakschroeder](https://github.com/izaakschroeder)
* Add `threshold` operation.
[#303](https://github.com/lovell/sharp/pull/303)
[@dacarley](https://github.com/dacarley)
* Add `negate` operation.
[#306](https://github.com/lovell/sharp/pull/306)
[@dacarley](https://github.com/dacarley)
* Support `options` Object with existing `extract` operation.
[#309](https://github.com/lovell/sharp/pull/309)
[@papandreou](https://github.com/papandreou)
### v0.11 - "*knife*" ### v0.11 - "*knife*"
#### v0.11.4 - 5<sup>th</sup> November 2015
* Add corners, e.g. `northeast`, to existing `gravity` option.
[#291](https://github.com/lovell/sharp/pull/291)
[@brandonaaron](https://github.com/brandonaaron)
* Ensure correct auto-rotation for EXIF Orientation values 2 and 4.
[#288](https://github.com/lovell/sharp/pull/288)
[@brandonaaron](https://github.com/brandonaaron)
* Make static linking possible via `--runtime_link` install option.
[#287](https://github.com/lovell/sharp/pull/287)
[@vlapo](https://github.com/vlapo)
#### v0.11.3 - 8<sup>th</sup> September 2015 #### v0.11.3 - 8<sup>th</sup> September 2015
* Intrepret blurSigma, sharpenFlat, and sharpenJagged as double precision. * Intrepret blurSigma, sharpenFlat, and sharpenJagged as double precision.

View File

@@ -7,6 +7,18 @@ 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 quickest ImageMagick and GraphicsMagick settings. the quickest ImageMagick and GraphicsMagick settings.
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.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
64-bit Windows and recent Linux systems do not require
the installation of any external runtime dependencies.
Use with OS X is as simple as running `brew install homebrew/science/vips`
to install the libvips dependency.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master) [![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
### Formats ### Formats
@@ -25,14 +37,6 @@ suitable for use with "slippy map" tile viewers like
[OpenSeadragon](https://github.com/openseadragon/openseadragon) [OpenSeadragon](https://github.com/openseadragon/openseadragon)
and [Leaflet](https://github.com/turban/Leaflet.Zoomify). and [Leaflet](https://github.com/turban/Leaflet.Zoomify).
### Features
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Colour spaces, embedded ICC profiles and alpha transparency channels
are all handled correctly.
### Fast ### Fast
This module is powered by the blazingly fast This module is powered by the blazingly fast
@@ -84,6 +88,7 @@ the help and code contributions of the following people:
* [Victor Mateevitsi](https://github.com/mvictoras) * [Victor Mateevitsi](https://github.com/mvictoras)
* [Alaric Holloway](https://github.com/skedastik) * [Alaric Holloway](https://github.com/skedastik)
* [Bernhard K. Weisshuhn](https://github.com/bkw) * [Bernhard K. Weisshuhn](https://github.com/bkw)
* [David A. Carley](https://github.com/dacarley)
Thank you! Thank you!

View File

@@ -6,74 +6,56 @@ npm install sharp
### Prerequisites ### Prerequisites
* Node.js v0.10+ or io.js * C++11 compatible compiler such as gcc 4.6+ (Node v4+ requires gcc 4.8+), clang 3.0+ or MSVC 2013
* [libvips](https://github.com/jcupitt/libvips) v7.40.0+ (7.42.0+ recommended) * [node-gyp](https://github.com/TooTallNate/node-gyp#installation)
* C++11 compatible compiler such as gcc 4.6+, clang 3.0+ or MSVC 2013 (io.js v3+ requires gcc 4.8+)
### Linux ### Linux
[![Ubuntu 12.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp) [![Ubuntu 14.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
[![Centos 6.5 Build Status](https://snap-ci.com/lovell/sharp/branch/master/build_image)](https://snap-ci.com/lovell/sharp/branch/master) [![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/lovell/sharp)
For a system-wide installation of the most suitable version of libvips and its dependencies are fetched and stored within `node_modules/sharp` during `npm install`.
libvips and its dependencies on the following Operating Systems: 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.:
* Debian 7, 8 * Debian 7, 8
* Ubuntu 12.04, 14.04, 14.10, 15.04 * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
* Mint 13, 17 * Centos 7
* RHEL/Centos/Scientific 6, 7 * Fedora 21, 22, 23
* Fedora 21, 22 * openSUSE 13.2
* Amazon Linux 2014.09, 2015.03 * Archlinux 2015.06.01
* OpenSuse 13 * Raspbian Jessie
run the following as a user with `sudo` access: Preference will be given to an existing globally-installed (via `pkg-config`)
version of libvips that meets the minimum version requirement.
This allows the use of newer versions of libvips with older versions of sharp.
For older Linux-based operating systems and 32-bit Intel CPUs,
a system-wide installation of the most suitable version of
libvips and its dependencies can be achieved by running
the following command as a user with `sudo` access
(requires `curl` and `pkg-config`):
```sh ```sh
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 -
``` ```
or run the following as `root`:
```sh
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | bash -
```
The [preinstall.sh](https://github.com/lovell/sharp/blob/master/preinstall.sh) script requires `curl` and `pkg-config`.
Add `--with-openslide` to enable OpenSlide support:
```sh
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -s -- --with-openslide
```
#### Ubuntu LTS
libvips v7.40.6 is available via a PPA.
##### 12.04
```sh
sudo add-apt-repository -y ppa:lovell/precise-backport-vips
sudo apt-get update
sudo apt-get install -y libvips-dev libgsf-1-dev
```
##### 14.04
```sh
sudo add-apt-repository -y ppa:lovell/trusty-backport-vips
sudo apt-get update
sudo apt-get install -y libvips-dev libgsf-1-dev
```
### 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)
Install libvips via homebrew: libvips must be installed before `npm install` is run.
This can be achieved via homebrew:
```sh ```sh
brew install homebrew/science/vips --with-webp --with-graphicsmagick brew install homebrew/science/vips
```
For GIF input and WebP output suppport use:
```sh
brew install homebrew/science/vips --with-imagemagick --with-webp
``` ```
A missing or incorrectly configured _Xcode Command Line Tools_ installation A missing or incorrectly configured _Xcode Command Line Tools_ installation
@@ -88,18 +70,22 @@ If so, please try `brew link gettext --force`.
### Windows ### Windows
[![Windows Server 2012 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp) [![Windows x64 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp)
Requires x86 32-bit Node.js or io.js (use `iojs.exe` rather than `node.exe`). libvips and its dependencies are fetched and stored within `node_modules\sharp` during `npm install`.
This involves an automated HTTPS download of approximately 9MB.
The WebP format is currently unsupported. Only 64-bit (x64) `node.exe` is supported.
The WebP format is currently unavailable on Windows.
1. Ensure the [node-gyp prerequisites](https://github.com/TooTallNate/node-gyp#installation) are met. ### FreeBSD
2. [Download](http://www.vips.ecs.soton.ac.uk/supported/current/win32/) and unzip `vips-dev.x.y.z.zip`.
3. Set the `VIPS_HOME` environment variable to the full path of the `vips-dev-x.y.z` directory.
4. Add `vips-dev-x.y.z\bin` to `PATH`.
Versions of MSVC more recent than 2013 may require the use of `npm install --arch=ia32 --msvs_version=2013`. libvips must be installed before `npm install` is run.
This can be achieved via [FreshPorts](https://www.freshports.org/graphics/vips/):
```sh
cd /usr/ports/graphics/vips/ && make install clean
```
### Heroku ### Heroku
@@ -109,13 +95,20 @@ and its dependencies.
### Docker ### Docker
[Marc Bachmann](https://github.com/marcbachmann) maintains a [Marc Bachmann](https://github.com/marcbachmann) maintains an
[Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips). [Ubuntu-based Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips).
```sh ```sh
docker pull marcbachmann/libvips docker pull marcbachmann/libvips
``` ```
[Will Jordan](https://github.com/wjordan) maintains an
[Alpine-based Dockerfile for libvips](https://github.com/wjordan/dockerfile-libvips).
```sh
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)

View File

@@ -2,39 +2,40 @@
### Test environment ### Test environment
* AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) * AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) (4x E5-2666 v3 @2.90GHz)
* Ubuntu 15.04 * Amazon Linux 2015.09.1
* Node.js 0.12.7 * Node.js v5.1.0
* libvips 8.0.2
* liborc 0.4.22
### The contenders ### The contenders
* [lwip](https://www.npmjs.com/package/lwip) 0.0.7 - Wrapper around CImg, compiles dependencies from source * [jimp](https://www.npmjs.com/package/jimp) v0.2.19 - Image processing in pure JavaScript. Bilinear interpolation only.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) git@45d4e2e - Wrapper around libmagick++, supports Buffers only. * [lwip](https://www.npmjs.com/package/lwip) v0.0.8 - Wrapper around CImg, compiles dependencies from source.
* [imagemagick](https://www.npmjs.com/package/imagemagick) 0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*". * [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) @5ab570e - Wrapper around libmagick++, supports Buffers only.
* [gm](https://www.npmjs.com/package/gm) 1.18.1 - Fully featured wrapper around GraphicsMagick's `convert` command line utility. * [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
* sharp 0.11.0 - Caching within libvips disabled to ensure a fair comparison. * [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.
### The task ### The task
Decompress a 2725x2225 JPEG image, resize to 720x480 using bilinear interpolation, then compress to JPEG. Decompress a 2725x2225 JPEG image, resize to 720x480 using bicubic interpolation (where available), then compress to JPEG.
### Results ### Results
| Module | Input | Output | Ops/sec | Speed-up | | Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: | | :----------------- | :----- | :----- | ------: | -------: |
| lwip | file | file | 1.75 | 1 | | jimp | file | file | 0.99 | 1.0 |
| lwip | buffer | buffer | 2.21 | 1.3 | | jimp | buffer | buffer | 1.05 | 1.1 |
| imagemagick-native | buffer | buffer | 7.13 | 4.1 | | lwip | file | file | 1.13 | 1.1 |
| gm | buffer | buffer | 7.27 | 4.2 | | lwip | buffer | buffer | 1.13 | 1.1 |
| gm | file | file | 7.33 | 4.2 | | imagemagick-native | buffer | buffer | 1.67 | 1.7 |
| imagemagick | file | file | 10.04 | 5.7 | | imagemagick | file | file | 5.19 | 5.2 |
| sharp | stream | stream | 23.12 | 13.2 | | gm | buffer | buffer | 5.56 | 5.6 |
| sharp | file | file | 24.43 | 14.0 | | gm | file | file | 5.59 | 5.6 |
| sharp | file | buffer | 24.55 | 14.0 | | sharp | stream | stream | 21.91 | 22.1 |
| sharp | buffer | file | 24.86 | 14.2 | | sharp | file | file | 22.79 | 23.0 |
| sharp | buffer | buffer | 24.92 | 14.2 | | sharp | file | buffer | 22.91 | 23.1 |
| sharp | buffer | file | 23.03 | 23.3 |
| sharp | buffer | buffer | 23.15 | 23.4 |
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.
@@ -50,14 +51,20 @@ brew install graphicsmagick
``` ```
```sh ```sh
sudo apt-get install imagemagick graphicsmagick libmagick++-dev sudo apt-get install imagemagick libmagick++-dev graphicsmagick
```
```sh
sudo yum install ImageMagick-devel ImageMagick-c++-devel GraphicsMagick
``` ```
### Running the benchmark test ### Running the benchmark test
```sh ```sh
git clone https://github.com/lovell/sharp.git git clone https://github.com/lovell/sharp.git
cd sharp/test/bench cd sharp
npm install
cd test/bench
npm install npm install
npm test npm test
``` ```

149
index.js Executable file → Normal file
View File

@@ -10,14 +10,31 @@ var color = require('color');
var BluebirdPromise = require('bluebird'); var BluebirdPromise = require('bluebird');
var sharp = require('./build/Release/sharp'); var sharp = require('./build/Release/sharp');
var libvipsVersion = sharp.libvipsVersion();
// Versioning
var versions = {
vips: sharp.libvipsVersion()
};
(function() {
// Does libvips meet minimum requirement?
var libvipsVersionMin = require('./package.json').config.libvips;
if (semver.lt(versions.vips, libvipsVersionMin)) {
throw new Error('Found libvips ' + versions.vips + ' but require at least ' + libvipsVersionMin);
}
// Include versions of dependencies, if present
try {
versions = require('./lib/versions.json');
} catch (err) {}
})();
// Limits
var maximum = { var maximum = {
width: 0x3FFF, width: 0x3FFF,
height: 0x3FFF, height: 0x3FFF,
pixels: Math.pow(0x3FFF, 2) pixels: Math.pow(0x3FFF, 2)
}; };
// Constructor-factory
var Sharp = function(input) { var Sharp = function(input) {
if (!(this instanceof Sharp)) { if (!(this instanceof Sharp)) {
return new Sharp(input); return new Sharp(input);
@@ -49,14 +66,16 @@ var Sharp = function(input) {
flip: false, flip: false,
flop: false, flop: false,
withoutEnlargement: false, withoutEnlargement: false,
interpolator: 'bilinear', interpolator: 'bicubic',
// operations // operations
background: [0, 0, 0, 255], background: [0, 0, 0, 255],
flatten: false, flatten: false,
negate: false,
blurSigma: 0, blurSigma: 0,
sharpenRadius: 0, sharpenRadius: 0,
sharpenFlat: 1, sharpenFlat: 1,
sharpenJagged: 2, sharpenJagged: 2,
threshold: 0,
gamma: 0, gamma: 0,
greyscale: false, greyscale: false,
normalize: 0, normalize: 0,
@@ -109,6 +128,11 @@ module.exports.queue = new events.EventEmitter();
*/ */
module.exports.format = sharp.format(); module.exports.format = sharp.format();
/*
Version numbers of libvips and its dependencies
*/
module.exports.versions = versions;
/* /*
Handle incoming chunk on Writable Stream Handle incoming chunk on Writable Stream
*/ */
@@ -137,11 +161,22 @@ Sharp.prototype._write = function(chunk, encoding, callback) {
}; };
// Crop this part of the resized image (Center/Centre, North, East, South, West) // Crop this part of the resized image (Center/Centre, North, East, South, West)
module.exports.gravity = {'center': 0, 'centre': 0, 'north': 1, 'east': 2, 'south': 3, 'west': 4}; module.exports.gravity = {
'center': 0,
'centre': 0,
'north': 1,
'east': 2,
'south': 3,
'west': 4,
'northeast': 5,
'southeast': 6,
'southwest': 7,
'northwest': 8
};
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 <= 4) { 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];
@@ -151,17 +186,25 @@ Sharp.prototype.crop = function(gravity) {
return this; return this;
}; };
Sharp.prototype.extract = function(topOffset, leftOffset, width, height) { Sharp.prototype.extract = function(options) {
/*jslint unused: false */ if (!options || typeof options !== 'object') {
// Legacy extract(top,left,width,height) syntax
options = {
left: arguments[1],
top: arguments[0],
width: arguments[2],
height: arguments[3]
};
}
var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post'; var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
var values = arguments; ['left', 'top', 'width', 'height'].forEach(function (name) {
['topOffset', 'leftOffset', 'width', 'height'].forEach(function(name, index) { var value = options[name];
if (typeof values[index] === 'number' && !Number.isNaN(values[index]) && (values[index] % 1 === 0) && values[index] >= 0) { if (typeof value === 'number' && !Number.isNaN(value) && value % 1 === 0 && value >= 0) {
this.options[name + suffix] = values[index]; this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
} else { } else {
throw new Error('Non-integer value for ' + name + ' of ' + values[index]); throw new Error('Non-integer value for ' + name + ' of ' + value);
} }
}.bind(this)); }, this);
// Ensure existing rotation occurs before pre-resize extraction // Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && this.options.angle !== 0) { if (suffix === 'Pre' && this.options.angle !== 0) {
this.options.rotateBeforePreExtract = true; this.options.rotateBeforePreExtract = true;
@@ -210,6 +253,11 @@ Sharp.prototype.flatten = function(flatten) {
return this; return this;
}; };
Sharp.prototype.negate = function(negate) {
this.options.negate = (typeof negate === 'boolean') ? negate : true;
return this;
};
Sharp.prototype.overlayWith = function(overlayPath) { Sharp.prototype.overlayWith = function(overlayPath) {
if (typeof overlayPath !== 'string') { if (typeof overlayPath !== 'string') {
throw new Error('The overlay path must be a string'); throw new Error('The overlay path must be a string');
@@ -323,6 +371,19 @@ Sharp.prototype.sharpen = function(radius, flat, jagged) {
return this; return this;
}; };
Sharp.prototype.threshold = function(threshold) {
if (typeof threshold === 'undefined') {
this.options.threshold = 128;
} else if (typeof threshold === 'boolean') {
this.options.threshold = threshold ? 128 : 0;
} else if (typeof threshold === 'number' && !Number.isNaN(threshold) && (threshold % 1 === 0) && threshold >= 0 && threshold <= 255) {
this.options.threshold = threshold;
} else {
throw new Error('Invalid threshold (0 to 255) ' + threshold);
}
return this;
};
/* /*
Set the interpolator to use for the affine transformation Set the interpolator to use for the affine transformation
*/ */
@@ -370,11 +431,7 @@ Sharp.prototype.gamma = function(gamma) {
Enhance output image contrast by stretching its luminance to cover the full dynamic range Enhance output image contrast by stretching its luminance to cover the full dynamic range
*/ */
Sharp.prototype.normalize = function(normalize) { Sharp.prototype.normalize = function(normalize) {
if (process.platform !== 'win32') { this.options.normalize = (typeof normalize === 'boolean') ? normalize : true;
this.options.normalize = (typeof normalize === 'boolean') ? normalize : true;
} else {
console.error('normalize unavailable on win32 platform');
}
return this; return this;
}; };
Sharp.prototype.normalise = Sharp.prototype.normalize; Sharp.prototype.normalise = Sharp.prototype.normalize;
@@ -420,14 +477,10 @@ Sharp.prototype.compressionLevel = function(compressionLevel) {
}; };
/* /*
Disable the use of adaptive row filtering for PNG output - requires libvips 7.42.0+ Disable the use of adaptive row filtering for PNG output
*/ */
Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) { Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) {
if (semver.gte(libvipsVersion, '7.42.0')) { this.options.withoutAdaptiveFiltering = (typeof withoutAdaptiveFiltering === 'boolean') ? withoutAdaptiveFiltering : true;
this.options.withoutAdaptiveFiltering = (typeof withoutAdaptiveFiltering === 'boolean') ? withoutAdaptiveFiltering : true;
} else {
console.error('withoutAdaptiveFiltering requires libvips 7.41.0+');
}
return this; return this;
}; };
@@ -440,41 +493,29 @@ Sharp.prototype.withoutChromaSubsampling = function(withoutChromaSubsampling) {
}; };
/* /*
Apply trellis quantisation to JPEG output - requires libvips 8.0.0+ compiled against mozjpeg 3.0+ Apply trellis quantisation to JPEG output - requires mozjpeg 3.0+
*/ */
Sharp.prototype.trellisQuantisation = function(trellisQuantisation) { Sharp.prototype.trellisQuantisation = function(trellisQuantisation) {
if (semver.gte(libvipsVersion, '8.0.0')) { this.options.trellisQuantisation = (typeof trellisQuantisation === 'boolean') ? trellisQuantisation : true;
this.options.trellisQuantisation = (typeof trellisQuantisation === 'boolean') ? trellisQuantisation : true;
} else {
console.error('trellisQuantisation requires libvips 8.0.0+');
}
return this; return this;
}; };
Sharp.prototype.trellisQuantization = Sharp.prototype.trellisQuantisation; Sharp.prototype.trellisQuantization = Sharp.prototype.trellisQuantisation;
/* /*
Apply overshoot deringing to JPEG output - requires libvips 8.0.0+ compiled against mozjpeg 3.0+ Apply overshoot deringing to JPEG output - requires mozjpeg 3.0+
*/ */
Sharp.prototype.overshootDeringing = function(overshootDeringing) { Sharp.prototype.overshootDeringing = function(overshootDeringing) {
if (semver.gte(libvipsVersion, '8.0.0')) { this.options.overshootDeringing = (typeof overshootDeringing === 'boolean') ? overshootDeringing : true;
this.options.overshootDeringing = (typeof overshootDeringing === 'boolean') ? overshootDeringing : true;
} else {
console.error('overshootDeringing requires libvips 8.0.0+');
}
return this; return this;
}; };
/* /*
Optimise scans in progressive JPEG output - requires libvips 8.0.0+ compiled against mozjpeg 3.0+ Optimise scans in progressive JPEG output - requires mozjpeg 3.0+
*/ */
Sharp.prototype.optimiseScans = function(optimiseScans) { Sharp.prototype.optimiseScans = function(optimiseScans) {
if (semver.gte(libvipsVersion, '8.0.0')) { this.options.optimiseScans = (typeof optimiseScans === 'boolean') ? optimiseScans : true;
this.options.optimiseScans = (typeof optimiseScans === 'boolean') ? optimiseScans : true; if (this.options.optimiseScans) {
if (this.options.optimiseScans) { this.progressive();
this.progressive();
}
} else {
console.error('optimiseScans requires libvips 8.0.0+');
} }
return this; return this;
}; };
@@ -493,7 +534,7 @@ Sharp.prototype.withMetadata = function(withMetadata) {
typeof withMetadata.orientation === 'number' && typeof withMetadata.orientation === 'number' &&
!Number.isNaN(withMetadata.orientation) && !Number.isNaN(withMetadata.orientation) &&
withMetadata.orientation % 1 === 0 && withMetadata.orientation % 1 === 0 &&
withMetadata.orientation >=0 && withMetadata.orientation >= 0 &&
withMetadata.orientation <= 7 withMetadata.orientation <= 7
) { ) {
this.options.withMetadataOrientation = withMetadata.orientation; this.options.withMetadataOrientation = withMetadata.orientation;
@@ -519,7 +560,7 @@ Sharp.prototype.tile = function(size, overlap) {
} }
// Overlap of tiles, in pixels // Overlap of tiles, in pixels
if (typeof overlap !== 'undefined' && overlap !== null) { if (typeof overlap !== 'undefined' && overlap !== null) {
if (!Number.isNaN(overlap) && overlap % 1 === 0 && overlap >=0 && overlap <= 8192) { if (!Number.isNaN(overlap) && overlap % 1 === 0 && overlap >= 0 && overlap <= 8192) {
if (overlap > this.options.tileSize) { if (overlap > this.options.tileSize) {
throw new Error('Tile overlap ' + overlap + ' cannot be larger than tile size ' + this.options.tileSize); throw new Error('Tile overlap ' + overlap + ' cannot be larger than tile size ' + this.options.tileSize);
} }
@@ -628,12 +669,7 @@ Sharp.prototype.webp = function() {
Force raw, uint8 output Force raw, uint8 output
*/ */
Sharp.prototype.raw = function() { Sharp.prototype.raw = function() {
var supportsRawOutput = module.exports.format.raw.output; this.options.output = '__raw';
if (supportsRawOutput.file || supportsRawOutput.buffer || supportsRawOutput.stream) {
this.options.output = '__raw';
} else {
console.error('Raw output requires libvips 7.42.0+');
}
return this; return this;
}; };
@@ -829,8 +865,13 @@ module.exports.counters = function() {
}; };
/* /*
Get the version of the libvips library Get and set use of SIMD vector unit instructions
*/ */
module.exports.libvipsVersion = function() { module.exports.simd = function(simd) {
return libvipsVersion; if (typeof simd !== 'boolean') {
simd = null;
}
return sharp.simd(simd);
}; };
// Switch off default
module.exports.simd(false);

View File

@@ -1,6 +1,6 @@
{ {
"name": "sharp", "name": "sharp",
"version": "0.11.3", "version": "0.12.2",
"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>",
@@ -17,16 +17,17 @@
"Victor Mateevitsi <mvictoras@gmail.com>", "Victor Mateevitsi <mvictoras@gmail.com>",
"Alaric Holloway <alaric.holloway@gmail.com>", "Alaric Holloway <alaric.holloway@gmail.com>",
"Bernhard K. Weisshuhn <bkw@codingforce.com>", "Bernhard K. Weisshuhn <bkw@codingforce.com>",
"Chris Riley <criley@primedia.com>" "Chris Riley <criley@primedia.com>",
"David Carley <dacarley@gmail.com>"
], ],
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library", "description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
"scripts": { "scripts": {
"clean": "rm -rf test/fixtures/output.*", "clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*",
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=20000 ./test/unit/*.js", "test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=30000 ./test/unit/*.js",
"test-clean": "npm run clean && npm install && npm test", "test-win": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=30000 ./test/unit/*.js",
"test-leak": "./test/leak/leak.sh", "test-leak": "./test/leak/leak.sh",
"test-win32-node": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=30000 ./test/unit/*.js", "test-packaging": "./packaging/test.sh",
"test-win32-iojs": "iojs ./node_modules/mocha/bin/mocha --slow=5000 --timeout=30000 ./test/unit/*.js" "test-clean": "rm -rf coverage/ test/fixtures/output.* && npm install && npm test"
}, },
"main": "index.js", "main": "index.js",
"repository": { "repository": {
@@ -46,24 +47,29 @@
"vips" "vips"
], ],
"dependencies": { "dependencies": {
"bluebird": "^2.9.34", "bluebird": "^3.1.1",
"color": "^0.10.1", "color": "^0.11.1",
"nan": "^2.0.8", "nan": "^2.2.0",
"semver": "^5.0.1" "semver": "^5.1.0",
"request": "^2.67.0",
"tar": "^2.2.1"
}, },
"devDependencies": { "devDependencies": {
"async": "^1.4.2", "async": "^1.5.2",
"coveralls": "^2.11.4", "coveralls": "^2.11.6",
"exif-reader": "1.0.0", "exif-reader": "^1.0.0",
"icc": "^0.0.2", "icc": "^0.0.2",
"istanbul": "^0.3.19", "istanbul": "^0.4.2",
"mocha": "^2.3.2", "mocha": "^2.3.4",
"mocha-jshint": "^2.2.3", "mocha-jshint": "^2.2.6",
"node-cpplint": "^0.4.0", "node-cpplint": "^0.4.0",
"rimraf": "^2.4.3", "rimraf": "^2.5.0",
"bufferutil": "^1.2.1" "bufferutil": "^1.2.1"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"config": {
"libvips": "8.2.0"
},
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"
} }

34
packaging/arm-build.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 IP"
echo "Build libvips for ARM using Docker, where IP is"
echo "the address of a Raspberry Pi running HypriotOS"
exit 1
fi
IP="$1"
echo "Verifying connectivity to $IP"
if ! ping -c 1 $IP; then
echo "Could not connect to $IP"
exit 1
fi
if ! type sshpass >/dev/null; then
echo "Please install sshpass"
exit 1
fi
export SSHPASS=hypriot
echo "Copying arm/Dockerfile and arm/build.sh to device"
sshpass -e scp -o PreferredAuthentications=password -r arm root@${IP}:/root
echo "Building Raspbian-based container"
sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker build -t vips-dev-arm arm"
echo "Running arm/build.sh within container"
sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker run -i -t --rm -v \${PWD}/arm:/arm vips-dev-arm sh -c 'cd /arm && ./build.sh' | tee arm/build.log"
echo "Copying resultant tar.gz file from device"
sshpass -e scp -o PreferredAuthentications=password root@${IP}:/root/arm/*.tar.gz .

28
packaging/arm-test.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 IP"
echo "Test sharp on ARM using Docker, where IP is"
echo "the address of a Raspberry Pi running HypriotOS"
exit 1
fi
IP="$1"
echo "Verifying connectivity to $IP"
if ! ping -c 1 $IP; then
echo "Could not connect to $IP"
exit 1
fi
if ! type sshpass >/dev/null; then
echo "Please install sshpass"
exit 1
fi
export SSHPASS=hypriot
echo "Copying sharp source to device"
sshpass -e scp -o PreferredAuthentications=password -r ../../sharp root@${IP}:/root/sharp
echo "Compile and test within container"
sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker run -i -t --rm -v \${PWD}/sharp:/s hypriot/rpi-node:5 sh -c 'cd /s && npm install --unsafe-perm && npm test'"

5
packaging/arm/Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM resin/rpi-raspbian:jessie
MAINTAINER Lovell Fuller <npm@lovell.info>
# Build dependencies
RUN apt-get update && apt-get install -y build-essential autoconf libtool nasm gtk-doc-tools texinfo curl

135
packaging/arm/build.sh Executable file
View File

@@ -0,0 +1,135 @@
#!/bin/sh
# To be run inside a Raspbian container
# Working directories
DEPS=/deps
TARGET=/target
mkdir ${DEPS}
mkdir ${TARGET}
# Common build paths and flags
export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig"
export PATH="${PATH}:${TARGET}/bin"
export CPPFLAGS="-I${TARGET}/include"
export LDFLAGS="-L${TARGET}/lib"
# Dependency version numbers
VERSION_ZLIB=1.2.8
VERSION_FFI=3.2.1
VERSION_GLIB=2.46.2
VERSION_XML2=2.9.3
VERSION_GSF=1.14.34
VERSION_EXIF=0.6.21
VERSION_JPEG=1.4.2
VERSION_PNG16=1.6.20
VERSION_LCMS2=2.7
VERSION_WEBP=0.5.0
VERSION_TIFF=4.0.6
VERSION_MAGICK=6.9.2-10
VERSION_ORC=0.4.24
VERSION_VIPS=8.2.0
mkdir ${DEPS}/zlib
curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
cd ${DEPS}/zlib
./configure --prefix=${TARGET} && make install
rm ${TARGET}/lib/libz.a
mkdir ${DEPS}/ffi
curl -Ls ftp://sourceware.org/pub/libffi/libffi-${VERSION_FFI}.tar.gz | tar xzC ${DEPS}/ffi --strip-components=1
cd ${DEPS}/ffi
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip
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
cd ${DEPS}/glib
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/xml2
curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1
cd ${DEPS}/xml2
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip
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
cd ${DEPS}/gsf
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/exif
curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
cd ${DEPS}/exif
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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
cd ${DEPS}/jpeg
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip
mkdir ${DEPS}/png16
curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
cd ${DEPS}/png16
./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
curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
cd ${DEPS}/webp
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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
cd ${DEPS}/tiff
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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
curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1
cd ${DEPS}/orc
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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
cd ${DEPS}/vips
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw \
--with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
&& make install-strip
# Remove the C++ bindings
cd ${TARGET}/include
rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h
cd ${TARGET}/lib
rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.*
# Create JSON file of version numbers
cd ${TARGET}
echo "{\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"magick\": \"${VERSION_MAGICK}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\
}" >lib/versions.json
# Create .tar.gz
GZIP=-9 tar czf /arm/libvips-${VERSION_VIPS}-arm.tar.gz include lib

28
packaging/build.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/sh
# Is docker available?
if ! type docker >/dev/null; then
echo "Please install docker"
exit 1
fi
# TODO: docker v1.9.0 will allow build-time args - https://github.com/docker/docker/pull/15182
# Windows
docker build -t vips-dev-win win
WIN_CONTAINER_ID=$(docker run -d vips-dev-win)
docker cp $WIN_CONTAINER_ID:/libvips-8.2.0-win.tar.gz .
docker rm $WIN_CONTAINER_ID
# Linux
docker build -t vips-dev-lin lin
LIN_CONTAINER_ID=$(docker run -d vips-dev-lin)
docker cp $LIN_CONTAINER_ID:/libvips-8.2.0-lin.tar.gz .
docker rm $LIN_CONTAINER_ID
# Checksums
sha256sum *.tar.gz

137
packaging/lin/Dockerfile Normal file
View File

@@ -0,0 +1,137 @@
FROM debian:wheezy
MAINTAINER Lovell Fuller <npm@lovell.info>
# Build dependencies
RUN apt-get update && apt-get install -y build-essential autoconf libtool nasm gtk-doc-tools texinfo
# Create working directories
ENV DEPS=/deps \
TARGET=/target
RUN mkdir ${DEPS} && mkdir ${TARGET}
# Common build paths and flags
ENV PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig \
PATH=${PATH}:${TARGET}/bin \
CPPFLAGS=-I${TARGET}/include \
LDFLAGS=-L${TARGET}/lib
# Dependency version numbers
ENV VERSION_ZLIB=1.2.8 \
VERSION_FFI=3.2.1 \
VERSION_GLIB=2.46.2 \
VERSION_XML2=2.9.3 \
VERSION_GSF=1.14.34 \
VERSION_EXIF=0.6.21 \
VERSION_JPEG=1.4.2 \
VERSION_PNG16=1.6.20 \
VERSION_LCMS2=2.7 \
VERSION_WEBP=0.5.0 \
VERSION_TIFF=4.0.6 \
VERSION_MAGICK=6.9.2-10 \
VERSION_ORC=0.4.24 \
VERSION_VIPS=8.2.0
RUN mkdir ${DEPS}/zlib
RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
WORKDIR ${DEPS}/zlib
RUN ./configure --prefix=${TARGET} && make install
RUN rm ${TARGET}/lib/libz.a
RUN mkdir ${DEPS}/ffi
RUN curl -Ls ftp://sourceware.org/pub/libffi/libffi-${VERSION_FFI}.tar.gz | tar xzC ${DEPS}/ffi --strip-components=1
WORKDIR ${DEPS}/ffi
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip
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
WORKDIR ${DEPS}/glib
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/xml2
RUN curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1
WORKDIR ${DEPS}/xml2
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip
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
WORKDIR ${DEPS}/gsf
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
RUN mkdir ${DEPS}/exif
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
WORKDIR ${DEPS}/exif
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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
WORKDIR ${DEPS}/jpeg
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip
RUN mkdir ${DEPS}/png16
RUN curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
WORKDIR ${DEPS}/png16
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 curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
WORKDIR ${DEPS}/webp
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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
WORKDIR ${DEPS}/tiff
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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 curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1
WORKDIR ${DEPS}/orc
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
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
WORKDIR ${DEPS}/vips
RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw \
--with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
&& make install-strip
# Remove the C++ bindings
WORKDIR ${TARGET}/include
RUN rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h
WORKDIR ${TARGET}/lib
RUN rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.*
# Create JSON file of version numbers
WORKDIR ${TARGET}
RUN echo "{\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"magick\": \"${VERSION_MAGICK}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\
}" >lib/versions.json
# Create .tar.gz
WORKDIR ${TARGET}
RUN GZIP=-9 tar czf /libvips-${VERSION_VIPS}-lin.tar.gz include lib

50
packaging/test.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/sh
# Verify docker is available
if ! type docker >/dev/null; then
echo "Please install docker"
exit 1
fi
test="npm run clean; NODE_ENV=development npm install --unsafe-perm; npm test"
# Debian 7, 8
# Ubuntu 12.04, 14.04
for dist in wheezy jessie precise trusty; do
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";
then echo "$dist OK"
else echo "$dist fail" && cat packaging/$dist.log
fi
done
# Centos 6
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";
then echo "centos6 OK"
else echo "centos6 fail" && cat packaging/centos6.log
fi
# Centos 7
# Fedora 20, 21
for dist in centos7 fedora20 fedora21; do
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";
then echo "$dist OK"
else echo "$dist fail" && cat packaging/$dist.log
fi
done
# openSUSE 13.2
echo "Testing opensuse..."
if docker run -i -t --rm -v $PWD:/v opensuse:13.2 >packaging/opensuse.log 2>&1 /bin/sh -c "cd /v; ./packaging/test/opensuse.sh; $test";
then echo "opensuse OK"
else echo "opensuse fail" && cat packaging/opensuse.log
fi
# Archlinux 2015.06.01
echo "Testing archlinux..."
if docker run -i -t --rm -v $PWD:/v base/archlinux:2015.06.01 >packaging/archlinux.log 2>&1 sh -c "cd /v; ./packaging/test/archlinux.sh; $test";
then echo "archlinux OK"
else echo "archlinux fail" && cat packaging/archlinux.log
fi

5
packaging/test/archlinux.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
# Install Node.js on Archlinux
pacman -Sy --noconfirm gcc make python2 nodejs npm | cat
ln -s /usr/bin/python2 /usr/bin/python

8
packaging/test/centos6.sh Executable file
View File

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

5
packaging/test/debian.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
# Install pkg-config on Debian/Ubuntu
apt-get update
apt-get install -y pkg-config

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

@@ -0,0 +1,7 @@
#!/bin/sh
# 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 --gpg-auto-import-keys refresh
zypper --non-interactive install gcc-c++ make nodejs-devel nodejs-npm
npm install -g npm

17
packaging/win/Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM ubuntu:precise
MAINTAINER Lovell Fuller <npm@lovell.info>
RUN apt-get update && apt-get install -y curl zip
# Fetch and unzip
RUN mkdir /vips
WORKDIR /vips
RUN curl -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.zip
RUN unzip vips-dev-w64-8.2.zip
# Clean and zip
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 cp bin/*.dll 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

View File

@@ -4,16 +4,18 @@
# Currently supports: # Currently supports:
# * Debian Linux # * Debian Linux
# * Debian 7, 8 # * Debian 7, 8
# * Ubuntu 12.04, 14.04, 14.10, 15.04 # * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
# * Mint 13, 17 # * Mint 13, 17
# * Elementary 0.3
# * Red Hat Linux # * Red Hat Linux
# * RHEL/Centos/Scientific 6, 7 # * RHEL/Centos/Scientific 6, 7
# * Fedora 21, 22 # * Fedora 21, 22, 23
# * Amazon Linux 2014.09, 2015.03 # * Amazon Linux 2015.03, 2015.09
# * OpenSuse 13
vips_version_minimum=7.40.0 vips_version_minimum=8.2.0
vips_version_latest_major_minor=8.0 vips_version_latest_major_minor=8.2
vips_version_latest_patch=2 vips_version_latest_patch=1
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
@@ -24,14 +26,14 @@ install_libvips_from_source() {
curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch
./configure --disable-debug --disable-docs --disable-static --disable-introspection --enable-cxx=yes --without-python --without-orc --without-fftw $1 ./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1
make make
make install make install
cd .. cd ..
rm -rf vips-$vips_version_latest_major_minor.$vips_version_latest_patch rm -rf vips-$vips_version_latest_major_minor.$vips_version_latest_patch
rm vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz rm vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
ldconfig ldconfig
echo "Installed libvips $vips_version_latest_major_minor.$vips_version_latest_patch" echo "Installed libvips $(PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig pkg-config --modversion vips)"
} }
install_libopenslide_from_source() { install_libopenslide_from_source() {
@@ -125,12 +127,12 @@ if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_ex
DISTRO=$(lsb_release -c -s) DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'" echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in case "$DISTRO" in
jessie|vivid) jessie|vivid|wily)
# Debian 8, Ubuntu 15 # Debian 8, Ubuntu 15
echo "Installing libopenslide via apt-get" echo "Installing libopenslide via apt-get"
apt-get install -y libopenslide-dev apt-get install -y libopenslide-dev
;; ;;
trusty|utopic|qiana|rebecca|rafaela) trusty|utopic|qiana|rebecca|rafaela|freya)
# Ubuntu 14, Mint 17 # Ubuntu 14, Mint 17
echo "Installing libopenslide dependencies via apt-get" echo "Installing libopenslide dependencies via apt-get"
apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
@@ -209,18 +211,8 @@ if [ -f /etc/debian_version ]; then
DISTRO=$(lsb_release -c -s) DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'" echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in case "$DISTRO" in
jessie|vivid) jessie|trusty|utopic|vivid|wily|xenial|qiana|rebecca|rafaela|freya)
# Debian 8, Ubuntu 15 # Debian 8, Ubuntu 14.04+, Mint 17
if [ $enable_openslide -eq 1 ]; then
echo "Recompiling vips with openslide support"
install_libvips_from_source
else
echo "Installing libvips via apt-get"
apt-get install -y libvips-dev libgsf-1-dev
fi
;;
trusty|utopic|qiana|rebecca|rafaela)
# Ubuntu 14, Mint 17
echo "Installing libvips dependencies via apt-get" echo "Installing libvips dependencies via apt-get"
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
install_libvips_from_source install_libvips_from_source
@@ -247,32 +239,26 @@ elif [ -f /etc/redhat-release ]; then
# RHEL/CentOS 7 # RHEL/CentOS 7
echo "Installing libvips dependencies via yum" echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools" yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
install_libvips_from_source "--prefix=/usr" install_libvips_from_source "--prefix=/usr"
;; ;;
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*) "Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
# RHEL/CentOS 6 # RHEL/CentOS 6
echo "Installing libvips dependencies via yum" echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools" yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel curl yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel
yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
yum install -y --enablerepo=nux-dextop gobject-introspection-devel yum install -y --enablerepo=nux-dextop gobject-introspection-devel
yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
yum install -y --enablerepo=remi libwebp-devel yum install -y --enablerepo=remi libwebp-devel
install_libvips_from_source "--prefix=/usr" install_libvips_from_source "--prefix=/usr"
;; ;;
"Fedora release 21 "*|"Fedora release 22 "*) "Fedora"*)
# Fedora 21, 22 # Fedora 21, 22, 23
if [ $enable_openslide -eq 1 ]; then echo "Installing libvips dependencies via yum"
echo "Installing libvips dependencies via yum" yum groupinstall -y "Development Tools"
yum groupinstall -y "Development Tools" yum install -y gcc-c++ gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
yum install -y gcc-c++ gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl install_libvips_from_source "--prefix=/usr"
echo "Compiling vips with openslide support"
install_libvips_from_source "--prefix=/usr"
else
echo "Installing libvips via yum"
yum install -y vips-devel
fi
;; ;;
*) *)
# Unsupported RHEL-based OS # Unsupported RHEL-based OS
@@ -283,12 +269,12 @@ elif [ -f /etc/system-release ]; then
# Probably Amazon Linux # Probably Amazon Linux
RELEASE=$(cat /etc/system-release) RELEASE=$(cat /etc/system-release)
case $RELEASE in case $RELEASE in
"Amazon Linux AMI release 2014.09"|"Amazon Linux AMI release 2015.03") "Amazon Linux AMI release 2015.03"|"Amazon Linux AMI release 2015.09")
# Amazon Linux # Amazon Linux
echo "Detected '$RELEASE'" echo "Detected '$RELEASE'"
echo "Installing libvips dependencies via yum" echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools" yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
install_libvips_from_source "--prefix=/usr" install_libvips_from_source "--prefix=/usr"
;; ;;
*) *)

25
src/common.cc Executable file → Normal file
View File

@@ -7,12 +7,8 @@
// Verify platform and compiler compatibility // Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 7 || (VIPS_MAJOR_VERSION == 7 && VIPS_MINOR_VERSION < 40)) #if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 1 && VIPS_PATCH_VERSION < 1))
#error libvips version 7.40.0+ required - see http://sharp.dimens.io/page/install #error libvips version 8.1.1+ required - see http://sharp.dimens.io/page/install
#endif
#ifdef _WIN64
#error Windows 64-bit is currently unsupported - see http://sharp.dimens.io/page/install#windows
#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)))
@@ -82,7 +78,7 @@ namespace sharp {
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF. 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) { VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access) {
return vips_image_new_from_buffer(buffer, length, NULL, "access", access, NULL); return vips_image_new_from_buffer(buffer, length, nullptr, "access", access, nullptr);
} }
/* /*
@@ -91,7 +87,7 @@ namespace sharp {
ImageType DetermineImageType(char const *file) { ImageType DetermineImageType(char const *file) {
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 != NULL) { if (load != nullptr) {
std::string loader = load; std::string loader = load;
if (EndsWith(loader, "JpegFile")) { if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG; imageType = ImageType::JPEG;
@@ -114,7 +110,7 @@ namespace sharp {
Initialise and return a VipsImage from a file. Initialise and return a VipsImage from a file.
*/ */
VipsImage* InitImage(char const *file, VipsAccess const access) { VipsImage* InitImage(char const *file, VipsAccess const access) {
return vips_image_new_from_file(file, "access", access, NULL); return vips_image_new_from_file(file, "access", access, nullptr);
} }
/* /*
@@ -173,7 +169,7 @@ namespace sharp {
*/ */
int InterpolatorWindowSize(char const *name) { int InterpolatorWindowSize(char const *name) {
VipsInterpolate *interpolator = vips_interpolate_new(name); VipsInterpolate *interpolator = vips_interpolate_new(name);
if (interpolator == NULL) { if (interpolator == nullptr) {
return -1; return -1;
} }
int window_size = vips_interpolate_get_window_size(interpolator); int window_size = vips_interpolate_get_window_size(interpolator);
@@ -181,4 +177,13 @@ namespace sharp {
return window_size; return window_size;
} }
/*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/
void FreeCallback(char* data, void* hint) {
if (data != nullptr) {
g_free(data);
}
}
} // namespace sharp } // namespace sharp

5
src/common.h Executable file → Normal file
View File

@@ -80,6 +80,11 @@ namespace sharp {
*/ */
int InterpolatorWindowSize(char const *name); int InterpolatorWindowSize(char const *name);
/*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/
void FreeCallback(char* data, void* hint);
} // namespace sharp } // namespace sharp
#endif // SRC_COMMON_H_ #endif // SRC_COMMON_H_

67
src/metadata.cc Executable file → Normal file
View File

@@ -35,6 +35,7 @@ using sharp::InitImage;
using sharp::HasProfile; using sharp::HasProfile;
using sharp::HasAlpha; using sharp::HasAlpha;
using sharp::ExifOrientation; using sharp::ExifOrientation;
using sharp::FreeCallback;
using sharp::counterQueue; using sharp::counterQueue;
struct MetadataBaton { struct MetadataBaton {
@@ -64,20 +65,15 @@ struct MetadataBaton {
iccLength(0) {} iccLength(0) {}
}; };
/*
Delete input char[] buffer and notify V8 of memory deallocation
Used as the callback function for the "postclose" signal
*/
static void DeleteBuffer(VipsObject *object, char *buffer) {
if (buffer != NULL) {
delete[] buffer;
}
}
class MetadataWorker : public AsyncWorker { class MetadataWorker : public AsyncWorker {
public: public:
MetadataWorker(Callback *callback, MetadataBaton *baton) : AsyncWorker(callback), baton(baton) {} MetadataWorker(Callback *callback, MetadataBaton *baton, const Local<Object> &bufferIn) :
AsyncWorker(callback), baton(baton) {
if (baton->bufferInLength > 0) {
SaveToPersistent("bufferIn", bufferIn);
}
}
~MetadataWorker() {} ~MetadataWorker() {}
void Execute() { void Execute() {
@@ -85,30 +81,25 @@ 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 = NULL; VipsImage *image = nullptr;
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); image = InitImage(baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
if (image != NULL) { if (image == nullptr) {
// Listen for "postclose" signal to delete input buffer
g_signal_connect(image, "postclose", G_CALLBACK(DeleteBuffer), baton->bufferIn);
} else {
(baton->err).append("Input buffer has corrupt header"); (baton->err).append("Input buffer has corrupt header");
imageType = ImageType::UNKNOWN; imageType = ImageType::UNKNOWN;
DeleteBuffer(NULL, baton->bufferIn);
} }
} else { } else {
(baton->err).append("Input buffer contains unsupported image format"); (baton->err).append("Input buffer contains unsupported image format");
DeleteBuffer(NULL, baton->bufferIn);
} }
} else { } else {
// From file // From file
imageType = DetermineImageType(baton->fileIn.c_str()); imageType = DetermineImageType(baton->fileIn.data());
if (imageType != ImageType::UNKNOWN) { if (imageType != ImageType::UNKNOWN) {
image = InitImage(baton->fileIn.c_str(), VIPS_ACCESS_RANDOM); image = InitImage(baton->fileIn.data(), VIPS_ACCESS_RANDOM);
if (image == NULL) { if (image == nullptr) {
(baton->err).append("Input file has corrupt header"); (baton->err).append("Input file has corrupt header");
imageType = ImageType::UNKNOWN; imageType = ImageType::UNKNOWN;
} }
@@ -116,7 +107,7 @@ class MetadataWorker : public AsyncWorker {
(baton->err).append("Input file is of an unsupported image format"); (baton->err).append("Input file is of an unsupported image format");
} }
} }
if (image != NULL && imageType != ImageType::UNKNOWN) { if (image != nullptr && imageType != ImageType::UNKNOWN) {
// Image type // Image type
switch (imageType) { switch (imageType) {
case ImageType::JPEG: baton->format = "jpeg"; break; case ImageType::JPEG: baton->format = "jpeg"; break;
@@ -142,7 +133,7 @@ class MetadataWorker : public AsyncWorker {
size_t exifLength; size_t exifLength;
if (!vips_image_get_blob(image, VIPS_META_EXIF_NAME, &exif, &exifLength)) { if (!vips_image_get_blob(image, VIPS_META_EXIF_NAME, &exif, &exifLength)) {
baton->exifLength = exifLength; baton->exifLength = exifLength;
baton->exif = static_cast<char*>(malloc(exifLength)); baton->exif = static_cast<char*>(g_malloc(exifLength));
memcpy(baton->exif, exif, exifLength); memcpy(baton->exif, exif, exifLength);
} }
} }
@@ -152,7 +143,7 @@ class MetadataWorker : public AsyncWorker {
size_t iccLength; size_t iccLength;
if (!vips_image_get_blob(image, VIPS_META_ICC_NAME, &icc, &iccLength)) { if (!vips_image_get_blob(image, VIPS_META_ICC_NAME, &icc, &iccLength)) {
baton->iccLength = iccLength; baton->iccLength = iccLength;
baton->icc = static_cast<char*>(malloc(iccLength)); baton->icc = static_cast<char*>(g_malloc(iccLength));
memcpy(baton->icc, icc, iccLength); memcpy(baton->icc, icc, iccLength);
} }
} }
@@ -170,7 +161,7 @@ class MetadataWorker : public AsyncWorker {
Local<Value> argv[2] = { Null(), Null() }; Local<Value> argv[2] = { Null(), Null() };
if (!baton->err.empty()) { if (!baton->err.empty()) {
// Error // Error
argv[0] = Error(baton->err.c_str()); argv[0] = Error(baton->err.data());
} else { } else {
// Metadata Object // Metadata Object
Local<Object> info = New<Object>(); Local<Object> info = New<Object>();
@@ -185,13 +176,24 @@ class MetadataWorker : public AsyncWorker {
Set(info, New("orientation").ToLocalChecked(), New<Number>(baton->orientation)); Set(info, New("orientation").ToLocalChecked(), New<Number>(baton->orientation));
} }
if (baton->exifLength > 0) { if (baton->exifLength > 0) {
Set(info, New("exif").ToLocalChecked(), NewBuffer(baton->exif, baton->exifLength).ToLocalChecked()); Set(info,
New("exif").ToLocalChecked(),
NewBuffer(baton->exif, baton->exifLength, FreeCallback, nullptr).ToLocalChecked()
);
} }
if (baton->iccLength > 0) { if (baton->iccLength > 0) {
Set(info, New("icc").ToLocalChecked(), NewBuffer(baton->icc, baton->iccLength).ToLocalChecked()); Set(info,
New("icc").ToLocalChecked(),
NewBuffer(baton->icc, baton->iccLength, FreeCallback, nullptr).ToLocalChecked()
);
} }
argv[1] = info; argv[1] = info;
} }
// Dispose of Persistent wrapper around input Buffer so it can be garbage collected
if (baton->bufferInLength > 0) {
GetFromPersistent("bufferIn");
}
delete baton; delete baton;
// Return to JavaScript // Return to JavaScript
@@ -215,17 +217,16 @@ NAN_METHOD(metadata) {
// Input filename // Input filename
baton->fileIn = *Utf8String(Get(options, New("fileIn").ToLocalChecked()).ToLocalChecked()); baton->fileIn = *Utf8String(Get(options, New("fileIn").ToLocalChecked()).ToLocalChecked());
// Input Buffer object // Input Buffer object
Local<Object> bufferIn;
if (node::Buffer::HasInstance(Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked())) { if (node::Buffer::HasInstance(Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked())) {
Local<Object> buffer = Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked().As<Object>(); bufferIn = Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked().As<Object>();
// Take a copy of the input Buffer to avoid problems with V8 heap compaction baton->bufferInLength = node::Buffer::Length(bufferIn);
baton->bufferInLength = node::Buffer::Length(buffer); baton->bufferIn = node::Buffer::Data(bufferIn);
baton->bufferIn = new char[baton->bufferInLength];
memcpy(baton->bufferIn, node::Buffer::Data(buffer), baton->bufferInLength);
} }
// Join queue for worker thread // Join queue for worker thread
Callback *callback = new Callback(info[1].As<Function>()); Callback *callback = new Callback(info[1].As<Function>());
AsyncQueueWorker(new MetadataWorker(callback, baton)); AsyncQueueWorker(new MetadataWorker(callback, baton, bufferIn));
// Increment queued task counter // Increment queued task counter
g_atomic_int_inc(&counterQueue); g_atomic_int_inc(&counterQueue);

143
src/operations.cc Executable file → Normal file
View File

@@ -14,11 +14,11 @@ namespace sharp {
// Split src into non-alpha and alpha // Split src into non-alpha and alpha
VipsImage *srcWithoutAlpha; VipsImage *srcWithoutAlpha;
if (vips_extract_band(src, &srcWithoutAlpha, 0, "n", src->Bands - 1, NULL)) if (vips_extract_band(src, &srcWithoutAlpha, 0, "n", src->Bands - 1, nullptr))
return -1; return -1;
vips_object_local(context, srcWithoutAlpha); vips_object_local(context, srcWithoutAlpha);
VipsImage *srcAlpha; VipsImage *srcAlpha;
if (vips_extract_band(src, &srcAlpha, src->Bands - 1, "n", 1, NULL)) if (vips_extract_band(src, &srcAlpha, src->Bands - 1, "n", 1, nullptr))
return -1; return -1;
vips_object_local(context, srcAlpha); vips_object_local(context, srcAlpha);
@@ -27,12 +27,12 @@ namespace sharp {
VipsImage *dstAlpha; VipsImage *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, NULL)) { if (vips_extract_band(dst, &dstWithoutAlpha, 0, "n", dst->Bands - 1, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, dstWithoutAlpha); vips_object_local(context, dstWithoutAlpha);
// Alpha: Extract last channel // Alpha: Extract last channel
if (vips_extract_band(dst, &dstAlpha, dst->Bands - 1, "n", 1, NULL)) { if (vips_extract_band(dst, &dstAlpha, dst->Bands - 1, "n", 1, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, dstAlpha); vips_object_local(context, dstAlpha);
@@ -41,11 +41,11 @@ namespace sharp {
dstWithoutAlpha = dst; dstWithoutAlpha = dst;
// Alpha: Use blank, opaque (0xFF) image // Alpha: Use blank, opaque (0xFF) image
VipsImage *black; VipsImage *black;
if (vips_black(&black, dst->Xsize, dst->Ysize, NULL)) { if (vips_black(&black, dst->Xsize, dst->Ysize, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, black); vips_object_local(context, black);
if (vips_invert(black, &dstAlpha, NULL)) { if (vips_invert(black, &dstAlpha, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, dstAlpha); vips_object_local(context, dstAlpha);
@@ -53,12 +53,12 @@ namespace sharp {
// Compute normalized input alpha channels: // Compute normalized input alpha channels:
VipsImage *srcAlphaNormalized; VipsImage *srcAlphaNormalized;
if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, NULL)) if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
return -1; return -1;
vips_object_local(context, srcAlphaNormalized); vips_object_local(context, srcAlphaNormalized);
VipsImage *dstAlphaNormalized; VipsImage *dstAlphaNormalized;
if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, NULL)) if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
return -1; return -1;
vips_object_local(context, dstAlphaNormalized); vips_object_local(context, dstAlphaNormalized);
@@ -75,17 +75,17 @@ namespace sharp {
// ^^^^^^^^^^^^^^^^^^^ // ^^^^^^^^^^^^^^^^^^^
// t1 // t1
VipsImage *t0; VipsImage *t0;
if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, NULL)) if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, nullptr))
return -1; return -1;
vips_object_local(context, t0); vips_object_local(context, t0);
VipsImage *t1; VipsImage *t1;
if (vips_multiply(dstAlphaNormalized, t0, &t1, NULL)) if (vips_multiply(dstAlphaNormalized, t0, &t1, nullptr))
return -1; return -1;
vips_object_local(context, t1); vips_object_local(context, t1);
VipsImage *outAlphaNormalized; VipsImage *outAlphaNormalized;
if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, NULL)) if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, nullptr))
return -1; return -1;
vips_object_local(context, outAlphaNormalized); vips_object_local(context, outAlphaNormalized);
@@ -102,92 +102,29 @@ namespace sharp {
// externally. // externally.
// //
VipsImage *t2; VipsImage *t2;
if (vips_multiply(dstWithoutAlpha, t0, &t2, NULL)) if (vips_multiply(dstWithoutAlpha, t0, &t2, nullptr))
return -1; return -1;
vips_object_local(context, t2); vips_object_local(context, t2);
VipsImage *outRGBPremultiplied; VipsImage *outRGBPremultiplied;
if (vips_add(srcWithoutAlpha, t2, &outRGBPremultiplied, NULL)) if (vips_add(srcWithoutAlpha, t2, &outRGBPremultiplied, nullptr))
return -1; return -1;
vips_object_local(context, outRGBPremultiplied); vips_object_local(context, outRGBPremultiplied);
// Denormalize output alpha channel: // Denormalize output alpha channel:
VipsImage *outAlpha; VipsImage *outAlpha;
if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, NULL)) if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, nullptr))
return -1; return -1;
vips_object_local(context, outAlpha); 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, NULL); return vips_bandjoin2(outRGBPremultiplied, outAlpha, out, nullptr);
}
/*
* Premultiply alpha channel of `image`.
*/
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
return vips_premultiply(image, out, NULL);
#else
VipsImage *imageRGB;
if (vips_extract_band(image, &imageRGB, 0, "n", image->Bands - 1, NULL))
return -1;
vips_object_local(context, imageRGB);
VipsImage *imageAlpha;
if (vips_extract_band(image, &imageAlpha, image->Bands - 1, "n", 1, NULL))
return -1;
vips_object_local(context, imageAlpha);
VipsImage *imageAlphaNormalized;
if (vips_linear1(imageAlpha, &imageAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
return -1;
vips_object_local(context, imageAlphaNormalized);
VipsImage *imageRGBPremultiplied;
if (vips_multiply(imageRGB, imageAlphaNormalized, &imageRGBPremultiplied, NULL))
return -1;
vips_object_local(context, imageRGBPremultiplied);
return vips_bandjoin2(imageRGBPremultiplied, imageAlpha, out, NULL);
#endif
}
/*
* Unpremultiply alpha channel of `image`.
*/
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
return vips_unpremultiply(image, out, NULL);
#else
VipsImage *imageRGBPremultipliedTransformed;
if (vips_extract_band(image, &imageRGBPremultipliedTransformed, 0, "n", image->Bands - 1, NULL))
return -1;
vips_object_local(context, imageRGBPremultipliedTransformed);
VipsImage *imageAlphaTransformed;
if (vips_extract_band(image, &imageAlphaTransformed, image->Bands - 1, "n", 1, NULL))
return -1;
vips_object_local(context, imageAlphaTransformed);
VipsImage *imageAlphaNormalizedTransformed;
if (vips_linear1(imageAlphaTransformed, &imageAlphaNormalizedTransformed, 1.0 / 255.0, 0.0, NULL))
return -1;
vips_object_local(context, imageAlphaNormalizedTransformed);
VipsImage *imageRGBUnpremultipliedTransformed;
if (vips_divide(imageRGBPremultipliedTransformed, imageAlphaNormalizedTransformed, &imageRGBUnpremultipliedTransformed, NULL))
return -1;
vips_object_local(context, imageRGBUnpremultipliedTransformed);
return vips_bandjoin2(imageRGBUnpremultipliedTransformed, imageAlphaTransformed, out, NULL);
#endif
} }
/* /*
* Stretch luminance to cover full dynamic range. * Stretch luminance to cover full dynamic range.
*/ */
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) { int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) {
#ifndef _WIN32
// Get original colourspace // Get original colourspace
VipsInterpretation typeBeforeNormalize = image->Type; VipsInterpretation typeBeforeNormalize = image->Type;
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) { if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
@@ -195,25 +132,25 @@ namespace sharp {
} }
// Convert to LAB colourspace // Convert to LAB colourspace
VipsImage *lab; VipsImage *lab;
if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, NULL)) { if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, lab); vips_object_local(context, lab);
// Extract luminance // Extract luminance
VipsImage *luminance; VipsImage *luminance;
if (vips_extract_band(lab, &luminance, 0, "n", 1, NULL)) { if (vips_extract_band(lab, &luminance, 0, "n", 1, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, luminance); vips_object_local(context, luminance);
// Extract chroma // Extract chroma
VipsImage *chroma; VipsImage *chroma;
if (vips_extract_band(lab, &chroma, 1, "n", 2, NULL)) { if (vips_extract_band(lab, &chroma, 1, "n", 2, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, chroma); vips_object_local(context, chroma);
// Find luminance range // Find luminance range
VipsImage *stats; VipsImage *stats;
if (vips_stats(luminance, &stats, NULL)) { if (vips_stats(luminance, &stats, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, stats); vips_object_local(context, stats);
@@ -224,19 +161,19 @@ namespace sharp {
double a = -(min * f); double a = -(min * f);
// Scale luminance // Scale luminance
VipsImage *luminance100; VipsImage *luminance100;
if (vips_linear1(luminance, &luminance100, f, a, NULL)) { if (vips_linear1(luminance, &luminance100, f, a, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, luminance100); vips_object_local(context, luminance100);
// Join scaled luminance to chroma // Join scaled luminance to chroma
VipsImage *normalizedLab; VipsImage *normalizedLab;
if (vips_bandjoin2(luminance100, chroma, &normalizedLab, NULL)) { if (vips_bandjoin2(luminance100, chroma, &normalizedLab, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, normalizedLab); vips_object_local(context, normalizedLab);
// Convert to original colourspace // Convert to original colourspace
VipsImage *normalized; VipsImage *normalized;
if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, NULL)) { if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, normalized); vips_object_local(context, normalized);
@@ -244,13 +181,13 @@ namespace sharp {
if (HasAlpha(image)) { if (HasAlpha(image)) {
// Extract original alpha channel // Extract original alpha channel
VipsImage *alpha; VipsImage *alpha;
if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, NULL)) { if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, alpha); vips_object_local(context, alpha);
// Join alpha channel to normalised image // Join alpha channel to normalised image
VipsImage *normalizedAlpha; VipsImage *normalizedAlpha;
if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, NULL)) { if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, normalizedAlpha); vips_object_local(context, normalizedAlpha);
@@ -262,11 +199,6 @@ namespace sharp {
// Cannot normalise zero-range image // Cannot normalise zero-range image
*out = image; *out = image;
} }
#else
// The normalize operation is currently unsupported on Windows
// See https://github.com/lovell/sharp/issues/152
*out = image;
#endif
return 0; return 0;
} }
@@ -283,19 +215,19 @@ namespace sharp {
1.0, 1.0, 1.0); 1.0, 1.0, 1.0);
vips_image_set_double(blur, "scale", 9); vips_image_set_double(blur, "scale", 9);
vips_object_local(context, blur); vips_object_local(context, blur);
if (vips_conv(image, &blurred, blur, NULL)) { if (vips_conv(image, &blurred, blur, nullptr)) {
return -1; return -1;
} }
} else { } else {
// Slower, accurate Gaussian blur // Slower, accurate Gaussian blur
// Create Gaussian function for standard deviation // Create Gaussian function for standard deviation
VipsImage *gaussian; VipsImage *gaussian;
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) { if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, nullptr)) {
return -1; return -1;
} }
vips_object_local(context, gaussian); vips_object_local(context, gaussian);
// Apply Gaussian function // Apply Gaussian function
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) { if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, nullptr)) {
return -1; return -1;
} }
} }
@@ -317,12 +249,12 @@ namespace sharp {
-1.0, -1.0, -1.0); -1.0, -1.0, -1.0);
vips_image_set_double(sharpen, "scale", 24); vips_image_set_double(sharpen, "scale", 24);
vips_object_local(context, sharpen); vips_object_local(context, sharpen);
if (vips_conv(image, &sharpened, sharpen, NULL)) { if (vips_conv(image, &sharpened, sharpen, nullptr)) {
return -1; 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, NULL)) { if (vips_sharpen(image, &sharpened, "radius", radius, "m1", flat, "m2", jagged, nullptr)) {
return -1; return -1;
} }
} }
@@ -330,4 +262,21 @@ namespace sharp {
*out = sharpened; *out = sharpened;
return 0; 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

View File

@@ -9,16 +9,6 @@ namespace sharp {
*/ */
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out); int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out);
/*
* Premultiply alpha channel of `image`.
*/
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out);
/*
* Unpremultiply alpha channel of `image`.
*/
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out);
/* /*
* Stretch luminance to cover full dynamic range. * Stretch luminance to cover full dynamic range.
*/ */
@@ -34,6 +24,11 @@ namespace sharp {
*/ */
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged); int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double 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_

408
src/pipeline.cc Executable file → Normal file
View File

@@ -32,16 +32,15 @@ using Nan::Get;
using Nan::Set; using Nan::Set;
using Nan::To; using Nan::To;
using Nan::New; using Nan::New;
using Nan::CopyBuffer; using Nan::NewBuffer;
using Nan::Null; using Nan::Null;
using Nan::Equals; using Nan::Equals;
using sharp::Composite; using sharp::Composite;
using sharp::Premultiply;
using sharp::Unpremultiply;
using sharp::Normalize; using sharp::Normalize;
using sharp::Blur; using sharp::Blur;
using sharp::Sharpen; using sharp::Sharpen;
using sharp::Threshold;
using sharp::ImageType; using sharp::ImageType;
using sharp::DetermineImageType; using sharp::DetermineImageType;
@@ -57,6 +56,7 @@ using sharp::IsPng;
using sharp::IsWebp; using sharp::IsWebp;
using sharp::IsTiff; using sharp::IsTiff;
using sharp::IsDz; using sharp::IsDz;
using sharp::FreeCallback;
using sharp::counterProcess; using sharp::counterProcess;
using sharp::counterQueue; using sharp::counterQueue;
@@ -101,10 +101,12 @@ struct PipelineBaton {
std::string interpolator; std::string interpolator;
double background[4]; double background[4];
bool flatten; bool flatten;
bool negate;
double blurSigma; double blurSigma;
int sharpenRadius; int sharpenRadius;
double sharpenFlat; double sharpenFlat;
double sharpenJagged; double sharpenJagged;
int threshold;
std::string overlayPath; std::string overlayPath;
double gamma; double gamma;
bool greyscale; bool greyscale;
@@ -139,10 +141,12 @@ struct PipelineBaton {
canvas(Canvas::CROP), canvas(Canvas::CROP),
gravity(0), gravity(0),
flatten(false), flatten(false),
negate(false),
blurSigma(0.0), blurSigma(0.0),
sharpenRadius(0), sharpenRadius(0),
sharpenFlat(1.0), sharpenFlat(1.0),
sharpenJagged(2.0), sharpenJagged(2.0),
threshold(0),
gamma(0.0), gamma(0.0),
greyscale(false), greyscale(false),
normalize(false), normalize(false),
@@ -169,21 +173,15 @@ struct PipelineBaton {
} }
}; };
/*
Delete input char[] buffer and notify V8 of memory deallocation
Used as the callback function for the "postclose" signal
*/
static void DeleteBuffer(VipsObject *object, char *buffer) {
if (buffer != NULL) {
delete[] buffer;
}
}
class PipelineWorker : public AsyncWorker { class PipelineWorker : public AsyncWorker {
public: public:
PipelineWorker(Callback *callback, PipelineBaton *baton, Callback *queueListener) : PipelineWorker(Callback *callback, PipelineBaton *baton, Callback *queueListener, const Local<Object> &bufferIn) :
AsyncWorker(callback), baton(baton), queueListener(queueListener) {} AsyncWorker(callback), baton(baton), queueListener(queueListener) {
if (baton->bufferInLength > 0) {
SaveToPersistent("bufferIn", bufferIn);
}
}
~PipelineWorker() {} ~PipelineWorker() {}
/* /*
@@ -204,31 +202,26 @@ class PipelineWorker : public AsyncWorker {
// Input // Input
ImageType inputImageType = ImageType::UNKNOWN; ImageType inputImageType = ImageType::UNKNOWN;
VipsImage *image = NULL; VipsImage *image = nullptr;
if (baton->bufferInLength > 0) { if (baton->bufferInLength > 0) {
// From buffer // From buffer
inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength); inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (inputImageType != ImageType::UNKNOWN) { if (inputImageType != ImageType::UNKNOWN) {
image = InitImage(baton->bufferIn, baton->bufferInLength, baton->accessMethod); image = InitImage(baton->bufferIn, baton->bufferInLength, baton->accessMethod);
if (image != NULL) { if (image == nullptr) {
// Listen for "postclose" signal to delete input buffer
g_signal_connect(image, "postclose", G_CALLBACK(DeleteBuffer), baton->bufferIn);
} else {
// Could not read header data // Could not read header data
(baton->err).append("Input buffer has corrupt header"); (baton->err).append("Input buffer has corrupt header");
inputImageType = ImageType::UNKNOWN; inputImageType = ImageType::UNKNOWN;
DeleteBuffer(NULL, baton->bufferIn);
} }
} else { } else {
(baton->err).append("Input buffer contains unsupported image format"); (baton->err).append("Input buffer contains unsupported image format");
DeleteBuffer(NULL, baton->bufferIn);
} }
} else { } else {
// From file // From file
inputImageType = DetermineImageType(baton->fileIn.c_str()); inputImageType = DetermineImageType(baton->fileIn.data());
if (inputImageType != ImageType::UNKNOWN) { if (inputImageType != ImageType::UNKNOWN) {
image = InitImage(baton->fileIn.c_str(), baton->accessMethod); image = InitImage(baton->fileIn.data(), baton->accessMethod);
if (image == NULL) { if (image == nullptr) {
(baton->err).append("Input file has corrupt header"); (baton->err).append("Input file has corrupt header");
inputImageType = ImageType::UNKNOWN; inputImageType = ImageType::UNKNOWN;
} }
@@ -236,7 +229,7 @@ class PipelineWorker : public AsyncWorker {
(baton->err).append("Input file is of an unsupported image format"); (baton->err).append("Input file is of an unsupported image format");
} }
} }
if (image == NULL || inputImageType == ImageType::UNKNOWN) { if (image == nullptr || inputImageType == ImageType::UNKNOWN) {
return Error(); return Error();
} }
vips_object_local(hook, image); vips_object_local(hook, image);
@@ -250,16 +243,21 @@ class PipelineWorker : public AsyncWorker {
// Calculate angle of rotation // Calculate angle of rotation
Angle rotation; Angle rotation;
bool flip; bool flip;
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image); bool flop;
std::tie(rotation, flip, flop) = CalculateRotationAndFlip(baton->angle, image);
if (flip && !baton->flip) { if (flip && !baton->flip) {
// Add flip operation due to EXIF mirroring // Add flip operation due to EXIF mirroring
baton->flip = TRUE; baton->flip = TRUE;
} }
if (flop && !baton->flop) {
// Add flip operation due to EXIF mirroring
baton->flop = TRUE;
}
// Rotate pre-extract // Rotate pre-extract
if (baton->rotateBeforePreExtract && rotation != Angle::D0) { if (baton->rotateBeforePreExtract && rotation != Angle::D0) {
VipsImage *rotated; VipsImage *rotated;
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) { if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, rotated); vips_object_local(hook, rotated);
@@ -270,7 +268,7 @@ class PipelineWorker : public AsyncWorker {
// Pre extraction // Pre extraction
if (baton->topOffsetPre != -1) { if (baton->topOffsetPre != -1) {
VipsImage *extractedPre; VipsImage *extractedPre;
if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, NULL)) { if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, extractedPre); vips_object_local(hook, extractedPre);
@@ -280,7 +278,7 @@ class PipelineWorker : public AsyncWorker {
// Get pre-resize image width and height // Get pre-resize image width and height
int inputWidth = image->Xsize; int inputWidth = image->Xsize;
int inputHeight = image->Ysize; int inputHeight = image->Ysize;
if (rotation == Angle::D90 || rotation == Angle::D270) { if (!baton->rotateBeforePreExtract && (rotation == Angle::D90 || rotation == Angle::D270)) {
// Swap input output width and height when rotating by 90 or 270 degrees // Swap input output width and height when rotating by 90 or 270 degrees
int swap = inputWidth; int swap = inputWidth;
inputWidth = inputHeight; inputWidth = inputHeight;
@@ -288,7 +286,7 @@ class PipelineWorker : public AsyncWorker {
} }
// Get window size of interpolator, used for determining shrink vs affine // Get window size of interpolator, used for determining shrink vs affine
int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.c_str()); int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.data());
if (interpolatorWindowSize < 0) { if (interpolatorWindowSize < 0) {
return Error(); return Error();
} }
@@ -407,11 +405,11 @@ class PipelineWorker : public AsyncWorker {
// Reload input using shrink-on-load // Reload input using shrink-on-load
VipsImage *shrunkOnLoad; VipsImage *shrunkOnLoad;
if (baton->bufferInLength > 1) { if (baton->bufferInLength > 1) {
if (vips_jpegload_buffer(baton->bufferIn, baton->bufferInLength, &shrunkOnLoad, "shrink", shrink_on_load, NULL)) { if (vips_jpegload_buffer(baton->bufferIn, baton->bufferInLength, &shrunkOnLoad, "shrink", shrink_on_load, nullptr)) {
return Error(); return Error();
} }
} else { } else {
if (vips_jpegload((baton->fileIn).c_str(), &shrunkOnLoad, "shrink", shrink_on_load, NULL)) { if (vips_jpegload((baton->fileIn).data(), &shrunkOnLoad, "shrink", shrink_on_load, nullptr)) {
return Error(); return Error();
} }
} }
@@ -423,7 +421,10 @@ class PipelineWorker : public AsyncWorker {
if (HasProfile(image)) { if (HasProfile(image)) {
// Convert to sRGB using embedded profile // Convert to sRGB using embedded profile
VipsImage *transformed; VipsImage *transformed;
if (!vips_icc_transform(image, &transformed, srgbProfile.c_str(), "embedded", TRUE, NULL)) { if (
!vips_icc_transform(image, &transformed, srgbProfile.data(),
"embedded", TRUE, "intent", VIPS_INTENT_PERCEPTUAL, nullptr)
) {
// Embedded profile can fail, so only update references on success // Embedded profile can fail, so only update references on success
vips_object_local(hook, transformed); vips_object_local(hook, transformed);
image = transformed; image = transformed;
@@ -432,24 +433,32 @@ class PipelineWorker : public AsyncWorker {
// Convert to sRGB using default "USWebCoatedSWOP" CMYK profile // Convert to sRGB using default "USWebCoatedSWOP" CMYK profile
std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc"; std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc";
VipsImage *transformed; VipsImage *transformed;
if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "input_profile", cmykProfile.c_str(), NULL)) { if (
vips_icc_transform(image, &transformed, srgbProfile.data(),
"input_profile", cmykProfile.data(), "intent", VIPS_INTENT_PERCEPTUAL, nullptr)
) {
return Error(); return Error();
} }
vips_object_local(hook, transformed); vips_object_local(hook, transformed);
image = transformed; image = transformed;
} }
// Calculate maximum alpha value based on input image pixel depth
double maxAlpha = (image->BandFmt == VIPS_FORMAT_USHORT) ? 65535.0 : 255.0;
// Flatten image to remove alpha channel // Flatten image to remove alpha channel
if (baton->flatten && HasAlpha(image)) { if (baton->flatten && HasAlpha(image)) {
// Scale up 8-bit values to match 16-bit input image
double multiplier = (image->Type == VIPS_INTERPRETATION_RGB16) ? 256.0 : 1.0;
// Background colour // Background colour
VipsArrayDouble *background = vips_array_double_newv( VipsArrayDouble *background = vips_array_double_newv(
3, // Ignore alpha channel as we're about to remove it 3, // Ignore alpha channel as we're about to remove it
baton->background[0], baton->background[0] * multiplier,
baton->background[1], baton->background[1] * multiplier,
baton->background[2] baton->background[2] * multiplier
); );
VipsImage *flattened; VipsImage *flattened;
if (vips_flatten(image, &flattened, "background", background, NULL)) { if (vips_flatten(image, &flattened, "background", background, "max_alpha", maxAlpha, nullptr)) {
vips_area_unref(reinterpret_cast<VipsArea*>(background)); vips_area_unref(reinterpret_cast<VipsArea*>(background));
return Error(); return Error();
} }
@@ -458,10 +467,20 @@ class PipelineWorker : public AsyncWorker {
image = flattened; image = flattened;
} }
// Negate the colors in the image.
if (baton->negate) {
VipsImage *negated;
if (vips_invert(image, &negated, nullptr)) {
return Error();
}
vips_object_local(hook, negated);
image = negated;
}
// Gamma encoding (darken) // Gamma encoding (darken)
if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) { if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) {
VipsImage *gammaEncoded; VipsImage *gammaEncoded;
if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, NULL)) { if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, gammaEncoded); vips_object_local(hook, gammaEncoded);
@@ -471,7 +490,7 @@ class PipelineWorker : public AsyncWorker {
// Convert to greyscale (linear, therefore after gamma encoding, if any) // Convert to greyscale (linear, therefore after gamma encoding, if any)
if (baton->greyscale) { if (baton->greyscale) {
VipsImage *greyscale; VipsImage *greyscale;
if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, NULL)) { if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, greyscale); vips_object_local(hook, greyscale);
@@ -481,7 +500,7 @@ class PipelineWorker : public AsyncWorker {
if (xshrink > 1 || yshrink > 1) { if (xshrink > 1 || yshrink > 1) {
VipsImage *shrunk; VipsImage *shrunk;
// Use vips_shrink with the integral reduction // Use vips_shrink with the integral reduction
if (vips_shrink(image, &shrunk, xshrink, yshrink, NULL)) { if (vips_shrink(image, &shrunk, xshrink, yshrink, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, shrunk); vips_object_local(hook, shrunk);
@@ -509,6 +528,7 @@ class PipelineWorker : public AsyncWorker {
bool shouldAffineTransform = xresidual != 0.0 || yresidual != 0.0; bool shouldAffineTransform = xresidual != 0.0 || yresidual != 0.0;
bool shouldBlur = baton->blurSigma != 0.0; bool shouldBlur = baton->blurSigma != 0.0;
bool shouldSharpen = baton->sharpenRadius != 0; bool shouldSharpen = baton->sharpenRadius != 0;
bool shouldThreshold = baton->threshold != 0;
bool hasOverlay = !baton->overlayPath.empty(); bool hasOverlay = !baton->overlayPath.empty();
bool shouldPremultiplyAlpha = HasAlpha(image) && (shouldAffineTransform || shouldBlur || shouldSharpen || hasOverlay); bool shouldPremultiplyAlpha = HasAlpha(image) && (shouldAffineTransform || shouldBlur || shouldSharpen || hasOverlay);
@@ -517,7 +537,7 @@ class PipelineWorker : public AsyncWorker {
// See: http://entropymine.com/imageworsener/resizealpha/ // See: http://entropymine.com/imageworsener/resizealpha/
if (shouldPremultiplyAlpha) { if (shouldPremultiplyAlpha) {
VipsImage *imagePremultiplied; VipsImage *imagePremultiplied;
if (Premultiply(hook, image, &imagePremultiplied)) { if (vips_premultiply(image, &imagePremultiplied, "max_alpha", maxAlpha, nullptr)) {
(baton->err).append("Failed to premultiply alpha channel."); (baton->err).append("Failed to premultiply alpha channel.");
return Error(); return Error();
} }
@@ -527,6 +547,12 @@ class PipelineWorker : public AsyncWorker {
// Use vips_affine with the remaining float part // Use vips_affine with the remaining float part
if (shouldAffineTransform) { if (shouldAffineTransform) {
// Create interpolator
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.data());
if (interpolator == nullptr) {
return Error();
}
vips_object_local(hook, interpolator);
// Use average of x and y residuals to compute sigma for Gaussian blur // Use average of x and y residuals to compute sigma for Gaussian blur
double residual = (xresidual + yresidual) / 2.0; double residual = (xresidual + yresidual) / 2.0;
// Apply Gaussian blur before large affine reductions // Apply Gaussian blur before large affine reductions
@@ -534,39 +560,31 @@ class PipelineWorker : public AsyncWorker {
// Calculate standard deviation // Calculate standard deviation
double sigma = ((1.0 / residual) - 0.4) / 3.0; double sigma = ((1.0 / residual) - 0.4) / 3.0;
if (sigma >= 0.3) { if (sigma >= 0.3) {
// Create Gaussian function for standard deviation
VipsImage *gaussian;
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) {
return Error();
}
vips_object_local(hook, gaussian);
// Sequential input requires a small linecache before use of convolution // Sequential input requires a small linecache before use of convolution
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) { if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) {
VipsImage *lineCached; VipsImage *lineCached;
if (vips_linecache(image, &lineCached, "access", VIPS_ACCESS_SEQUENTIAL, "tile_height", 1, "threaded", TRUE, NULL)) { if (vips_linecache(image, &lineCached, "access", VIPS_ACCESS_SEQUENTIAL,
"tile_height", 1, "threaded", TRUE, nullptr)
) {
return Error(); return Error();
} }
vips_object_local(hook, lineCached); vips_object_local(hook, lineCached);
image = lineCached; image = lineCached;
} }
// Apply Gaussian function // Apply Gaussian blur
VipsImage *blurred; VipsImage *blurred;
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) { if (vips_gaussblur(image, &blurred, sigma, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, blurred); vips_object_local(hook, blurred);
image = blurred; image = blurred;
} }
} }
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
if (interpolator == NULL) {
return Error();
}
vips_object_local(hook, interpolator);
// Perform affine transformation // Perform affine transformation
VipsImage *affined; VipsImage *affined;
if (vips_affine(image, &affined, xresidual, 0.0, 0.0, yresidual, "interpolate", interpolator, NULL)) { if (vips_affine(image, &affined, xresidual, 0.0, 0.0, yresidual,
"interpolate", interpolator, nullptr)
) {
return Error(); return Error();
} }
vips_object_local(hook, affined); vips_object_local(hook, affined);
@@ -576,7 +594,7 @@ class PipelineWorker : public AsyncWorker {
// Rotate // Rotate
if (!baton->rotateBeforePreExtract && rotation != Angle::D0) { if (!baton->rotateBeforePreExtract && rotation != Angle::D0) {
VipsImage *rotated; VipsImage *rotated;
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) { if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, rotated); vips_object_local(hook, rotated);
@@ -587,7 +605,7 @@ class PipelineWorker : public AsyncWorker {
// Flip (mirror about Y axis) // Flip (mirror about Y axis)
if (baton->flip) { if (baton->flip) {
VipsImage *flipped; VipsImage *flipped;
if (vips_flip(image, &flipped, VIPS_DIRECTION_VERTICAL, NULL)) { if (vips_flip(image, &flipped, VIPS_DIRECTION_VERTICAL, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, flipped); vips_object_local(hook, flipped);
@@ -598,7 +616,7 @@ class PipelineWorker : public AsyncWorker {
// Flop (mirror about X axis) // Flop (mirror about X axis)
if (baton->flop) { if (baton->flop) {
VipsImage *flopped; VipsImage *flopped;
if (vips_flip(image, &flopped, VIPS_DIRECTION_HORIZONTAL, NULL)) { if (vips_flip(image, &flopped, VIPS_DIRECTION_HORIZONTAL, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, flopped); vips_object_local(hook, flopped);
@@ -609,33 +627,23 @@ class PipelineWorker : public AsyncWorker {
// Crop/embed // Crop/embed
if (image->Xsize != baton->width || image->Ysize != baton->height) { if (image->Xsize != baton->width || image->Ysize != baton->height) {
if (baton->canvas == Canvas::EMBED) { if (baton->canvas == Canvas::EMBED) {
// Match background colour space, namely sRGB
if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Convert to sRGB colour space
VipsImage *colourspaced;
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error();
}
vips_object_local(hook, colourspaced);
image = colourspaced;
}
// Add non-transparent alpha channel, if required // Add non-transparent alpha channel, if required
if (baton->background[3] < 255.0 && !HasAlpha(image)) { if (baton->background[3] < 255.0 && !HasAlpha(image)) {
// Create single-channel transparency // Create single-channel transparency
VipsImage *black; VipsImage *black;
if (vips_black(&black, image->Xsize, image->Ysize, "bands", 1, NULL)) { if (vips_black(&black, image->Xsize, image->Ysize, "bands", 1, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, black); vips_object_local(hook, black);
// Invert to become non-transparent // Invert to become non-transparent
VipsImage *alpha; VipsImage *alpha;
if (vips_invert(black, &alpha, NULL)) { if (vips_invert(black, &alpha, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, alpha); vips_object_local(hook, alpha);
// Append alpha channel to existing image // Append alpha channel to existing image
VipsImage *joined; VipsImage *joined;
if (vips_bandjoin2(image, alpha, &joined, NULL)) { if (vips_bandjoin2(image, alpha, &joined, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, joined); vips_object_local(hook, joined);
@@ -643,13 +651,22 @@ class PipelineWorker : public AsyncWorker {
} }
// Create background // Create background
VipsArrayDouble *background; VipsArrayDouble *background;
// Scale up 8-bit values to match 16-bit input image
double multiplier = (image->Type == VIPS_INTERPRETATION_RGB16) ? 256.0 : 1.0;
if (baton->background[3] < 255.0 || HasAlpha(image)) { if (baton->background[3] < 255.0 || HasAlpha(image)) {
background = vips_array_double_newv( // RGBA
4, baton->background[0], baton->background[1], baton->background[2], baton->background[3] background = vips_array_double_newv(4,
baton->background[0] * multiplier,
baton->background[1] * multiplier,
baton->background[2] * multiplier,
baton->background[3] * multiplier
); );
} else { } else {
background = vips_array_double_newv( // RGB
3, baton->background[0], baton->background[1], baton->background[2] background = vips_array_double_newv(3,
baton->background[0] * multiplier,
baton->background[1] * multiplier,
baton->background[2] * multiplier
); );
} }
// Embed // Embed
@@ -657,7 +674,7 @@ class PipelineWorker : public AsyncWorker {
int top = (baton->height - image->Ysize) / 2; int top = (baton->height - image->Ysize) / 2;
VipsImage *embedded; VipsImage *embedded;
if (vips_embed(image, &embedded, left, top, baton->width, baton->height, if (vips_embed(image, &embedded, left, top, baton->width, baton->height,
"extend", VIPS_EXTEND_BACKGROUND, "background", background, NULL "extend", VIPS_EXTEND_BACKGROUND, "background", background, nullptr
)) { )) {
vips_area_unref(reinterpret_cast<VipsArea*>(background)); vips_area_unref(reinterpret_cast<VipsArea*>(background));
return Error(); return Error();
@@ -673,7 +690,7 @@ class PipelineWorker : public AsyncWorker {
int width = std::min(image->Xsize, baton->width); int width = std::min(image->Xsize, baton->width);
int height = std::min(image->Ysize, baton->height); int height = std::min(image->Ysize, baton->height);
VipsImage *extracted; VipsImage *extracted;
if (vips_extract_area(image, &extracted, left, top, width, height, NULL)) { if (vips_extract_area(image, &extracted, left, top, width, height, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, extracted); vips_object_local(hook, extracted);
@@ -685,7 +702,7 @@ class PipelineWorker : public AsyncWorker {
if (baton->topOffsetPost != -1) { if (baton->topOffsetPost != -1) {
VipsImage *extractedPost; VipsImage *extractedPost;
if (vips_extract_area(image, &extractedPost, if (vips_extract_area(image, &extractedPost,
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, nullptr
)) { )) {
return Error(); return Error();
} }
@@ -693,6 +710,15 @@ class PipelineWorker : public AsyncWorker {
image = extractedPost; image = extractedPost;
} }
// Threshold - must happen before blurring, due to the utility of blurring after thresholding
if (shouldThreshold) {
VipsImage *thresholded;
if (Threshold(hook, image, &thresholded, baton->threshold)) {
return Error();
}
image = thresholded;
}
// Blur // Blur
if (shouldBlur) { if (shouldBlur) {
VipsImage *blurred; VipsImage *blurred;
@@ -713,12 +739,12 @@ class PipelineWorker : public AsyncWorker {
// Composite with overlay, if present // Composite with overlay, if present
if (hasOverlay) { if (hasOverlay) {
VipsImage *overlayImage = NULL; VipsImage *overlayImage = nullptr;
ImageType overlayImageType = ImageType::UNKNOWN; ImageType overlayImageType = ImageType::UNKNOWN;
overlayImageType = DetermineImageType(baton->overlayPath.c_str()); overlayImageType = DetermineImageType(baton->overlayPath.data());
if (overlayImageType != ImageType::UNKNOWN) { if (overlayImageType != ImageType::UNKNOWN) {
overlayImage = InitImage(baton->overlayPath.c_str(), baton->accessMethod); overlayImage = InitImage(baton->overlayPath.data(), baton->accessMethod);
if (overlayImage == NULL) { if (overlayImage == nullptr) {
(baton->err).append("Overlay image has corrupt header"); (baton->err).append("Overlay image has corrupt header");
return Error(); return Error();
} else { } else {
@@ -749,15 +775,15 @@ class PipelineWorker : public AsyncWorker {
// Ensure overlay is sRGB // Ensure overlay is sRGB
VipsImage *overlayImageRGB; VipsImage *overlayImageRGB;
if (vips_colourspace(overlayImage, &overlayImageRGB, VIPS_INTERPRETATION_sRGB, NULL)) { if (vips_colourspace(overlayImage, &overlayImageRGB, VIPS_INTERPRETATION_sRGB, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, overlayImageRGB); vips_object_local(hook, overlayImageRGB);
// Premultiply overlay // Premultiply overlay
VipsImage *overlayImagePremultiplied; VipsImage *overlayImagePremultiplied;
if (Premultiply(hook, overlayImageRGB, &overlayImagePremultiplied)) { if (vips_premultiply(overlayImageRGB, &overlayImagePremultiplied, nullptr)) {
(baton->err).append("Failed to premultiply alpha channel of overlay image."); (baton->err).append("Failed to premultiply alpha channel of overlay image");
return Error(); return Error();
} }
vips_object_local(hook, overlayImagePremultiplied); vips_object_local(hook, overlayImagePremultiplied);
@@ -774,11 +800,10 @@ class PipelineWorker : public AsyncWorker {
// Reverse premultiplication after all transformations: // Reverse premultiplication after all transformations:
if (shouldPremultiplyAlpha) { if (shouldPremultiplyAlpha) {
VipsImage *imageUnpremultiplied; VipsImage *imageUnpremultiplied;
if (Unpremultiply(hook, image, &imageUnpremultiplied)) { if (vips_unpremultiply(image, &imageUnpremultiplied, "max_alpha", maxAlpha, nullptr)) {
(baton->err).append("Failed to unpremultiply alpha channel."); (baton->err).append("Failed to unpremultiply alpha channel");
return Error(); return Error();
} }
vips_object_local(hook, imageUnpremultiplied); vips_object_local(hook, imageUnpremultiplied);
image = imageUnpremultiplied; image = imageUnpremultiplied;
} }
@@ -786,7 +811,7 @@ class PipelineWorker : public AsyncWorker {
// Gamma decoding (brighten) // Gamma decoding (brighten)
if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) { if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) {
VipsImage *gammaDecoded; VipsImage *gammaDecoded;
if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, NULL)) { if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, gammaDecoded); vips_object_local(hook, gammaDecoded);
@@ -803,10 +828,27 @@ class PipelineWorker : public AsyncWorker {
} }
// Convert image to sRGB, if not already // Convert image to sRGB, if not already
if (image->Type != VIPS_INTERPRETATION_sRGB) { if (image->Type == VIPS_INTERPRETATION_RGB16) {
// Ensure 16-bit integer
VipsImage *ushort;
if (vips_cast_ushort(image, &ushort, nullptr)) {
return Error();
}
vips_object_local(hook, ushort);
image = ushort;
// Fast conversion to 8-bit integer by discarding least-significant byte
VipsImage *msb;
if (vips_msb(image, &msb, nullptr)) {
return Error();
}
vips_object_local(hook, msb);
image = msb;
// Explicitly set to sRGB
image->Type = VIPS_INTERPRETATION_sRGB;
} else if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Switch interpretation to sRGB // Switch interpretation to sRGB
VipsImage *rgb; VipsImage *rgb;
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, NULL)) { if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, rgb); vips_object_local(hook, rgb);
@@ -814,7 +856,7 @@ class PipelineWorker : public AsyncWorker {
// Transform colours from embedded profile to sRGB profile // Transform colours from embedded profile to sRGB profile
if (baton->withMetadata && HasProfile(image)) { if (baton->withMetadata && HasProfile(image)) {
VipsImage *profiled; VipsImage *profiled;
if (vips_icc_transform(image, &profiled, srgbProfile.c_str(), "embedded", TRUE, NULL)) { if (vips_icc_transform(image, &profiled, srgbProfile.data(), "embedded", TRUE, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, profiled); vips_object_local(hook, profiled);
@@ -827,63 +869,54 @@ class PipelineWorker : public AsyncWorker {
SetExifOrientation(image, baton->withMetadataOrientation); SetExifOrientation(image, baton->withMetadataOrientation);
} }
#if !(VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5))
// Generate image tile cache when interlace output is required - no longer required as of libvips 7.40.5+
if (baton->progressive) {
VipsImage *cached;
if (vips_tilecache(image, &cached, "threaded", TRUE, "persistent", TRUE, "max_tiles", -1, NULL)) {
return Error();
}
vips_object_local(hook, cached);
image = cached;
}
#endif
// Output // Output
if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) { if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) {
// Write JPEG to buffer // Write JPEG to buffer
if (vips_jpegsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata, if (vips_jpegsave_buffer(
"Q", baton->quality, "optimize_coding", TRUE, "no_subsample", baton->withoutChromaSubsampling, image, &baton->bufferOut, &baton->bufferOutLength,
#if (VIPS_MAJOR_VERSION >= 8) "strip", !baton->withMetadata,
"Q", baton->quality,
"optimize_coding", TRUE,
"no_subsample", baton->withoutChromaSubsampling,
"trellis_quant", baton->trellisQuantisation, "trellis_quant", baton->trellisQuantisation,
"overshoot_deringing", baton->overshootDeringing, "overshoot_deringing", baton->overshootDeringing,
"optimize_scans", baton->optimiseScans, "optimize_scans", baton->optimiseScans,
#endif "interlace", baton->progressive,
"interlace", baton->progressive, NULL)) { nullptr
)) {
return Error(); return Error();
} }
baton->outputFormat = "jpeg"; baton->outputFormat = "jpeg";
} else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) { } else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) {
#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42))
// Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
// Write PNG to buffer // Write PNG to buffer
if (vips_pngsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata, if (vips_pngsave_buffer(
"compression", baton->compressionLevel, "interlace", baton->progressive, "filter", filter, NULL)) { image, &baton->bufferOut, &baton->bufferOutLength,
"strip", !baton->withMetadata,
"compression", baton->compressionLevel,
"interlace", baton->progressive,
"filter", baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL,
nullptr
)) {
return Error(); return Error();
} }
#else
// Write PNG to buffer
if (vips_pngsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
return Error();
}
#endif
baton->outputFormat = "png"; baton->outputFormat = "png";
} else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == ImageType::WEBP)) { } else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == ImageType::WEBP)) {
// Write WEBP to buffer // Write WEBP to buffer
if (vips_webpsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata, if (vips_webpsave_buffer(
"Q", baton->quality, NULL)) { image, &baton->bufferOut, &baton->bufferOutLength,
"strip", !baton->withMetadata,
"Q", baton->quality,
nullptr
)) {
return Error(); return Error();
} }
baton->outputFormat = "webp"; baton->outputFormat = "webp";
#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42))
} else if (baton->output == "__raw") { } else if (baton->output == "__raw") {
// Write raw, uncompressed image data to buffer // Write raw, uncompressed image data to buffer
if (baton->greyscale || image->Type == VIPS_INTERPRETATION_B_W) { if (baton->greyscale || image->Type == VIPS_INTERPRETATION_B_W) {
// Extract first band for greyscale image // Extract first band for greyscale image
VipsImage *grey; VipsImage *grey;
if (vips_extract_band(image, &grey, 0, NULL)) { if (vips_extract_band(image, &grey, 0, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, grey); vips_object_local(hook, grey);
@@ -892,7 +925,7 @@ class PipelineWorker : public AsyncWorker {
if (image->BandFmt != VIPS_FORMAT_UCHAR) { if (image->BandFmt != VIPS_FORMAT_UCHAR) {
// Cast pixels to uint8 (unsigned char) // Cast pixels to uint8 (unsigned char)
VipsImage *uchar; VipsImage *uchar;
if (vips_cast(image, &uchar, VIPS_FORMAT_UCHAR, NULL)) { if (vips_cast(image, &uchar, VIPS_FORMAT_UCHAR, nullptr)) {
return Error(); return Error();
} }
vips_object_local(hook, uchar); vips_object_local(hook, uchar);
@@ -900,12 +933,11 @@ class PipelineWorker : public AsyncWorker {
} }
// Get raw image data // Get raw image data
baton->bufferOut = vips_image_write_to_memory(image, &baton->bufferOutLength); baton->bufferOut = vips_image_write_to_memory(image, &baton->bufferOutLength);
if (baton->bufferOut == NULL) { if (baton->bufferOut == nullptr) {
(baton->err).append("Could not allocate enough memory for raw output"); (baton->err).append("Could not allocate enough memory for raw output");
return Error(); return Error();
} }
baton->outputFormat = "raw"; baton->outputFormat = "raw";
#endif
} else { } else {
bool outputJpeg = IsJpeg(baton->output); bool outputJpeg = IsJpeg(baton->output);
bool outputPng = IsPng(baton->output); bool outputPng = IsPng(baton->output);
@@ -915,52 +947,66 @@ class PipelineWorker : public AsyncWorker {
bool matchInput = !(outputJpeg || outputPng || outputWebp || outputTiff || outputDz); bool matchInput = !(outputJpeg || outputPng || outputWebp || outputTiff || outputDz);
if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) { if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
// Write JPEG to file // Write JPEG to file
if (vips_jpegsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_jpegsave(
"Q", baton->quality, "optimize_coding", TRUE, "no_subsample", baton->withoutChromaSubsampling, image, baton->output.data(),
#if (VIPS_MAJOR_VERSION >= 8) "strip", !baton->withMetadata,
"Q", baton->quality,
"optimize_coding", TRUE,
"no_subsample", baton->withoutChromaSubsampling,
"trellis_quant", baton->trellisQuantisation, "trellis_quant", baton->trellisQuantisation,
"overshoot_deringing", baton->overshootDeringing, "overshoot_deringing", baton->overshootDeringing,
"optimize_scans", baton->optimiseScans, "optimize_scans", baton->optimiseScans,
#endif "interlace", baton->progressive,
"interlace", baton->progressive, NULL)) { nullptr
)) {
return Error(); return Error();
} }
baton->outputFormat = "jpeg"; baton->outputFormat = "jpeg";
} else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) { } else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) {
#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42))
// Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
// Write PNG to file // Write PNG to file
if (vips_pngsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_pngsave(
"compression", baton->compressionLevel, "interlace", baton->progressive, "filter", filter, NULL)) { image, baton->output.data(),
"strip", !baton->withMetadata,
"compression", baton->compressionLevel,
"interlace", baton->progressive,
"filter", baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL,
nullptr
)) {
return Error(); return Error();
} }
#else
// Write PNG to file
if (vips_pngsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
return Error();
}
#endif
baton->outputFormat = "png"; baton->outputFormat = "png";
} else if (outputWebp || (matchInput && inputImageType == ImageType::WEBP)) { } else if (outputWebp || (matchInput && inputImageType == ImageType::WEBP)) {
// Write WEBP to file // Write WEBP to file
if (vips_webpsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_webpsave(
"Q", baton->quality, NULL)) { image, baton->output.data(),
"strip", !baton->withMetadata,
"Q", baton->quality,
nullptr
)) {
return Error(); return Error();
} }
baton->outputFormat = "webp"; baton->outputFormat = "webp";
} else if (outputTiff || (matchInput && inputImageType == ImageType::TIFF)) { } else if (outputTiff || (matchInput && inputImageType == ImageType::TIFF)) {
// Write TIFF to file // Write TIFF to file
if (vips_tiffsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_tiffsave(
"compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, "Q", baton->quality, NULL)) { image, baton->output.data(),
"strip", !baton->withMetadata,
"compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG,
"Q", baton->quality,
nullptr
)) {
return Error(); return Error();
} }
baton->outputFormat = "tiff"; baton->outputFormat = "tiff";
} else if (outputDz) { } else if (outputDz) {
// Write DZ to file // Write DZ to file
if (vips_dzsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_dzsave(
"tile_size", baton->tileSize, "overlap", baton->tileOverlap, NULL)) { image, baton->output.data(),
"strip", !baton->withMetadata,
"tile_size", baton->tileSize,
"overlap", baton->tileOverlap,
nullptr
)) {
return Error(); return Error();
} }
baton->outputFormat = "dz"; baton->outputFormat = "dz";
@@ -979,10 +1025,10 @@ class PipelineWorker : public AsyncWorker {
void HandleOKCallback () { void HandleOKCallback () {
HandleScope(); HandleScope();
Local<Value> argv[3] = { Null(), Null(), Null() }; Local<Value> argv[3] = { Null(), Null(), Null() };
if (!baton->err.empty()) { if (!baton->err.empty()) {
// Error // Error
argv[0] = Nan::Error(baton->err.c_str()); argv[0] = Nan::Error(baton->err.data());
} else { } else {
int width = baton->width; int width = baton->width;
int height = baton->height; int height = baton->height;
@@ -1001,23 +1047,27 @@ class PipelineWorker : public AsyncWorker {
Set(info, New("height").ToLocalChecked(), New<Uint32>(static_cast<uint32_t>(height))); Set(info, New("height").ToLocalChecked(), New<Uint32>(static_cast<uint32_t>(height)));
if (baton->bufferOutLength > 0) { if (baton->bufferOutLength > 0) {
// Copy data to new Buffer // Pass ownership of output data to Buffer instance
argv[1] = CopyBuffer(static_cast<char*>(baton->bufferOut), baton->bufferOutLength).ToLocalChecked(); argv[1] = NewBuffer(
// bufferOut was allocated via g_malloc static_cast<char*>(baton->bufferOut), baton->bufferOutLength, FreeCallback, nullptr
g_free(baton->bufferOut); ).ToLocalChecked();
// Add buffer size to info // Add buffer size to info
Set(info, New("size").ToLocalChecked(), New<Uint32>(static_cast<uint32_t>(baton->bufferOutLength))); Set(info, New("size").ToLocalChecked(), New<Uint32>(static_cast<uint32_t>(baton->bufferOutLength)));
argv[2] = info; argv[2] = info;
} else { } else {
// Add file size to info // Add file size to info
GStatBuf st; GStatBuf st;
g_stat(baton->output.c_str(), &st); g_stat(baton->output.data(), &st);
Set(info, New("size").ToLocalChecked(), New<Uint32>(static_cast<uint32_t>(st.st_size))); Set(info, New("size").ToLocalChecked(), New<Uint32>(static_cast<uint32_t>(st.st_size)));
argv[1] = info; argv[1] = info;
} }
} }
// Dispose of Persistent wrapper around input Buffer so it can be garbage collected
if (baton->bufferInLength > 0) {
GetFromPersistent("bufferIn");
}
delete baton; delete baton;
// to here
// Decrement processing task counter // Decrement processing task counter
g_atomic_int_dec_and_test(&counterProcess); g_atomic_int_dec_and_test(&counterProcess);
@@ -1041,18 +1091,19 @@ class PipelineWorker : public AsyncWorker {
2. Use input image EXIF Orientation header - supports mirroring 2. Use input image EXIF Orientation header - supports mirroring
3. Otherwise default to zero, i.e. no rotation 3. Otherwise default to zero, i.e. no rotation
*/ */
std::tuple<Angle, bool> std::tuple<Angle, bool, bool>
CalculateRotationAndFlip(int const angle, VipsImage const *input) { CalculateRotationAndFlip(int const angle, VipsImage const *input) {
Angle rotate = Angle::D0; Angle rotate = Angle::D0;
bool flip = FALSE; bool flip = FALSE;
bool flop = FALSE;
if (angle == -1) { if (angle == -1) {
switch(ExifOrientation(input)) { switch(ExifOrientation(input)) {
case 6: rotate = Angle::D90; break; case 6: rotate = Angle::D90; break;
case 3: rotate = Angle::D180; break; case 3: rotate = Angle::D180; break;
case 8: rotate = Angle::D270; break; case 8: rotate = Angle::D270; break;
case 2: flip = TRUE; break; // flip 1 case 2: flop = TRUE; break; // flop 1
case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6 case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6
case 4: flip = TRUE; rotate = Angle::D180; break; // flip 3 case 4: flop = TRUE; rotate = Angle::D180; break; // flop 3
case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8 case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8
} }
} else { } else {
@@ -1064,7 +1115,7 @@ class PipelineWorker : public AsyncWorker {
rotate = Angle::D270; rotate = Angle::D270;
} }
} }
return std::make_tuple(rotate, flip); return std::make_tuple(rotate, flip, flop);
} }
/* /*
@@ -1090,6 +1141,16 @@ class PipelineWorker : public AsyncWorker {
case 4: // West case 4: // West
top = (inHeight - outHeight + 1) / 2; top = (inHeight - outHeight + 1) / 2;
break; break;
case 5: // Northeast
left = inWidth - outWidth;
break;
case 6: // Southeast
left = inWidth - outWidth;
top = inHeight - outHeight;
case 7: // Southwest
top = inHeight - outHeight;
case 8: // Northwest
break;
default: // Centre default: // Centre
left = (inWidth - outWidth + 1) / 2; left = (inWidth - outWidth + 1) / 2;
top = (inHeight - outHeight + 1) / 2; top = (inHeight - outHeight + 1) / 2;
@@ -1153,12 +1214,11 @@ NAN_METHOD(pipeline) {
To<bool>(Get(options, New("sequentialRead").ToLocalChecked()).ToLocalChecked()).FromJust() ? To<bool>(Get(options, New("sequentialRead").ToLocalChecked()).ToLocalChecked()).FromJust() ?
VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM; VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Input Buffer object // Input Buffer object
Local<Object> bufferIn;
if (node::Buffer::HasInstance(Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked())) { if (node::Buffer::HasInstance(Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked())) {
Local<Object> buffer = Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked().As<Object>(); bufferIn = Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked().As<Object>();
// Take a copy of the input Buffer to avoid problems with V8 heap compaction baton->bufferInLength = node::Buffer::Length(bufferIn);
baton->bufferInLength = node::Buffer::Length(buffer); baton->bufferIn = node::Buffer::Data(bufferIn);
baton->bufferIn = new char[baton->bufferInLength];
memcpy(baton->bufferIn, node::Buffer::Data(buffer), baton->bufferInLength);
} }
// ICC profile to use when input CMYK image has no embedded profile // ICC profile to use when input CMYK image has no embedded profile
baton->iccProfilePath = *Utf8String(Get(options, New("iccProfilePath").ToLocalChecked()).ToLocalChecked()); baton->iccProfilePath = *Utf8String(Get(options, New("iccProfilePath").ToLocalChecked()).ToLocalChecked());
@@ -1202,10 +1262,12 @@ NAN_METHOD(pipeline) {
baton->interpolator = *Utf8String(Get(options, New("interpolator").ToLocalChecked()).ToLocalChecked()); baton->interpolator = *Utf8String(Get(options, New("interpolator").ToLocalChecked()).ToLocalChecked());
// Operators // Operators
baton->flatten = To<bool>(Get(options, New("flatten").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->flatten = To<bool>(Get(options, New("flatten").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->negate = To<bool>(Get(options, New("negate").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->blurSigma = To<double>(Get(options, New("blurSigma").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->blurSigma = To<double>(Get(options, New("blurSigma").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->sharpenRadius = To<int32_t>(Get(options, New("sharpenRadius").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->sharpenRadius = To<int32_t>(Get(options, New("sharpenRadius").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->sharpenFlat = To<double>(Get(options, New("sharpenFlat").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->sharpenFlat = To<double>(Get(options, New("sharpenFlat").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->sharpenJagged = To<double>(Get(options, New("sharpenJagged").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->sharpenJagged = To<double>(Get(options, New("sharpenJagged").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->threshold = To<int32_t>(Get(options, New("threshold").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->gamma = To<int32_t>(Get(options, New("gamma").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->gamma = To<int32_t>(Get(options, New("gamma").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->greyscale = To<bool>(Get(options, New("greyscale").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->greyscale = To<bool>(Get(options, New("greyscale").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->normalize = To<bool>(Get(options, New("normalize").ToLocalChecked()).ToLocalChecked()).FromJust(); baton->normalize = To<bool>(Get(options, New("normalize").ToLocalChecked()).ToLocalChecked()).FromJust();
@@ -1233,7 +1295,7 @@ NAN_METHOD(pipeline) {
// Join queue for worker thread // Join queue for worker thread
Callback *callback = new Callback(info[1].As<Function>()); Callback *callback = new Callback(info[1].As<Function>());
AsyncQueueWorker(new PipelineWorker(callback, baton, queueListener)); AsyncQueueWorker(new PipelineWorker(callback, baton, queueListener, bufferIn));
// Increment queued task counter // Increment queued task counter
g_atomic_int_inc(&counterQueue); g_atomic_int_inc(&counterQueue);

2
src/sharp.cc Executable file → Normal file
View File

@@ -26,6 +26,8 @@ NAN_MODULE_INIT(init) {
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(concurrency)).ToLocalChecked()); Nan::GetFunction(Nan::New<v8::FunctionTemplate>(concurrency)).ToLocalChecked());
Nan::Set(target, Nan::New("counters").ToLocalChecked(), Nan::Set(target, Nan::New("counters").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(counters)).ToLocalChecked()); Nan::GetFunction(Nan::New<v8::FunctionTemplate>(counters)).ToLocalChecked());
Nan::Set(target, Nan::New("simd").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(simd)).ToLocalChecked());
Nan::Set(target, Nan::New("libvipsVersion").ToLocalChecked(), Nan::Set(target, Nan::New("libvipsVersion").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(libvipsVersion)).ToLocalChecked()); Nan::GetFunction(Nan::New<v8::FunctionTemplate>(libvipsVersion)).ToLocalChecked());
Nan::Set(target, Nan::New("format").ToLocalChecked(), Nan::Set(target, Nan::New("format").ToLocalChecked(),

66
src/utilities.cc Executable file → Normal file
View File

@@ -1,5 +1,7 @@
#include <cmath>
#include <node.h> #include <node.h>
#include <vips/vips.h> #include <vips/vips.h>
#include <vips/vector.h>
#include "nan.h" #include "nan.h"
@@ -7,11 +9,12 @@
#include "operations.h" #include "operations.h"
#include "utilities.h" #include "utilities.h"
using v8::Local;
using v8::Object;
using v8::Number;
using v8::String;
using v8::Boolean; using v8::Boolean;
using v8::Integer;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using Nan::HandleScope; using Nan::HandleScope;
using Nan::New; using Nan::New;
@@ -38,10 +41,16 @@ NAN_METHOD(cache) {
// Get cache statistics // Get cache statistics
Local<Object> cache = New<Object>(); Local<Object> cache = New<Object>();
Set(cache, New("current").ToLocalChecked(), New<Number>(vips_tracked_get_mem() / 1048576)); Set(cache, New("current").ToLocalChecked(),
Set(cache, New("high").ToLocalChecked(), New<Number>(vips_tracked_get_mem_highwater() / 1048576)); New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576)))
Set(cache, New("memory").ToLocalChecked(), New<Number>(vips_cache_get_max_mem() / 1048576)); );
Set(cache, New("items").ToLocalChecked(), New<Number>(vips_cache_get_max())); Set(cache, New("high").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576)))
);
Set(cache, New("memory").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576)))
);
Set(cache, New("items").ToLocalChecked(), New<Integer>(vips_cache_get_max()));
info.GetReturnValue().Set(cache); info.GetReturnValue().Set(cache);
} }
@@ -56,7 +65,7 @@ NAN_METHOD(concurrency) {
vips_concurrency_set(To<int32_t>(info[0]).FromJust()); vips_concurrency_set(To<int32_t>(info[0]).FromJust());
} }
// Get concurrency // Get concurrency
info.GetReturnValue().Set(New<Number>(vips_concurrency_get())); info.GetReturnValue().Set(New<Integer>(vips_concurrency_get()));
} }
/* /*
@@ -68,11 +77,25 @@ NAN_METHOD(counters) {
HandleScope(); HandleScope();
Local<Object> counters = New<Object>(); Local<Object> counters = New<Object>();
Set(counters, New("queue").ToLocalChecked(), New<Number>(counterQueue)); Set(counters, New("queue").ToLocalChecked(), New<Integer>(counterQueue));
Set(counters, New("process").ToLocalChecked(), New<Number>(counterProcess)); Set(counters, New("process").ToLocalChecked(), New<Integer>(counterProcess));
info.GetReturnValue().Set(counters); info.GetReturnValue().Set(counters);
} }
/*
Get and set use of SIMD vector unit instructions
*/
NAN_METHOD(simd) {
HandleScope();
// Set state
if (info[0]->IsBoolean()) {
vips_vector_set_enabled(To<bool>(info[0]).FromJust());
}
// Get state
info.GetReturnValue().Set(New<Boolean>(vips_vector_isenabled()));
}
/* /*
Get libvips version Get libvips version
*/ */
@@ -157,7 +180,6 @@ 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 sharp::Premultiply;
using sharp::DetermineImageType; using sharp::DetermineImageType;
using sharp::ImageType; using sharp::ImageType;
using sharp::InitImage; using sharp::InitImage;
@@ -169,11 +191,11 @@ NAN_METHOD(_maxColourDistance) {
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new()); VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
// Open input files // Open input files
VipsImage *image1 = NULL; VipsImage *image1 = nullptr;
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); image1 = InitImage(*Utf8String(info[0]), VIPS_ACCESS_SEQUENTIAL);
if (image1 == NULL) { if (image1 == nullptr) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError("Input file 1 has corrupt header"); return ThrowError("Input file 1 has corrupt header");
} else { } else {
@@ -183,11 +205,11 @@ NAN_METHOD(_maxColourDistance) {
g_object_unref(hook); 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 = NULL; VipsImage *image2 = nullptr;
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); image2 = InitImage(*Utf8String(info[1]), VIPS_ACCESS_SEQUENTIAL);
if (image2 == NULL) { if (image2 == nullptr) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError("Input file 2 has corrupt header"); return ThrowError("Input file 2 has corrupt header");
} else { } else {
@@ -212,13 +234,13 @@ NAN_METHOD(_maxColourDistance) {
// Premultiply and remove alpha // Premultiply and remove alpha
if (HasAlpha(image1)) { if (HasAlpha(image1)) {
VipsImage *imagePremultiplied1; VipsImage *imagePremultiplied1;
if (Premultiply(hook, image1, &imagePremultiplied1)) { if (vips_premultiply(image1, &imagePremultiplied1, nullptr)) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError(vips_error_buffer()); return ThrowError(vips_error_buffer());
} }
vips_object_local(hook, imagePremultiplied1); vips_object_local(hook, imagePremultiplied1);
VipsImage *imagePremultipliedNoAlpha1; VipsImage *imagePremultipliedNoAlpha1;
if (vips_extract_band(image1, &imagePremultipliedNoAlpha1, 1, "n", image1->Bands - 1, NULL)) { if (vips_extract_band(image1, &imagePremultipliedNoAlpha1, 1, "n", image1->Bands - 1, nullptr)) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError(vips_error_buffer()); return ThrowError(vips_error_buffer());
} }
@@ -227,13 +249,13 @@ NAN_METHOD(_maxColourDistance) {
} }
if (HasAlpha(image2)) { if (HasAlpha(image2)) {
VipsImage *imagePremultiplied2; VipsImage *imagePremultiplied2;
if (Premultiply(hook, image2, &imagePremultiplied2)) { if (vips_premultiply(image2, &imagePremultiplied2, nullptr)) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError(vips_error_buffer()); return ThrowError(vips_error_buffer());
} }
vips_object_local(hook, imagePremultiplied2); vips_object_local(hook, imagePremultiplied2);
VipsImage *imagePremultipliedNoAlpha2; VipsImage *imagePremultipliedNoAlpha2;
if (vips_extract_band(image2, &imagePremultipliedNoAlpha2, 1, "n", image2->Bands - 1, NULL)) { if (vips_extract_band(image2, &imagePremultipliedNoAlpha2, 1, "n", image2->Bands - 1, nullptr)) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError(vips_error_buffer()); return ThrowError(vips_error_buffer());
} }
@@ -242,14 +264,14 @@ NAN_METHOD(_maxColourDistance) {
} }
// Calculate colour distance // Calculate colour distance
VipsImage *difference; VipsImage *difference;
if (vips_dE00(image1, image2, &difference, NULL)) { if (vips_dE00(image1, image2, &difference, nullptr)) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError(vips_error_buffer()); return ThrowError(vips_error_buffer());
} }
vips_object_local(hook, difference); vips_object_local(hook, difference);
// Extract maximum distance // Extract maximum distance
double maxColourDistance; double maxColourDistance;
if (vips_max(difference, &maxColourDistance, NULL)) { if (vips_max(difference, &maxColourDistance, nullptr)) {
g_object_unref(hook); g_object_unref(hook);
return ThrowError(vips_error_buffer()); return ThrowError(vips_error_buffer());
} }

1
src/utilities.h Executable file → Normal file
View File

@@ -6,6 +6,7 @@
NAN_METHOD(cache); NAN_METHOD(cache);
NAN_METHOD(concurrency); NAN_METHOD(concurrency);
NAN_METHOD(counters); NAN_METHOD(counters);
NAN_METHOD(simd);
NAN_METHOD(libvipsVersion); NAN_METHOD(libvipsVersion);
NAN_METHOD(format); NAN_METHOD(format);
NAN_METHOD(_maxColourDistance); NAN_METHOD(_maxColourDistance);

13
test/bench/package.json Executable file → Normal file
View File

@@ -8,13 +8,14 @@
"test": "node perf && node random && node parallel" "test": "node perf && node random && node parallel"
}, },
"devDependencies": { "devDependencies": {
"async": "^1.5.0",
"benchmark": "^1.0.0",
"gm": "^1.21.0",
"imagemagick": "^0.1.3", "imagemagick": "^0.1.3",
"imagemagick-native": "^1.8.0", "imagemagick-native": "elad/node-imagemagick-native",
"gm": "^1.18.1", "jimp": "^0.2.20",
"lwip": "^0.0.7", "lwip": "^0.0.8",
"async": "^1.4.2", "semver": "^5.1.0"
"semver": "^5.0.1",
"benchmark": "^1.0.0"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {

5
test/bench/parallel.js Executable file → Normal file
View File

@@ -1,5 +1,7 @@
'use strict'; 'use strict';
process.env.UV_THREADPOOL_SIZE = 64;
var assert = require('assert'); var assert = require('assert');
var async = require('async'); var async = require('async');
@@ -10,12 +12,13 @@ var width = 720;
var height = 480; var height = 480;
sharp.concurrency(1); sharp.concurrency(1);
sharp.simd(true);
var timer = setInterval(function() { var timer = setInterval(function() {
console.dir(sharp.counters()); console.dir(sharp.counters());
}, 100); }, 100);
async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64, 128], function(parallelism, next) { async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64], function(parallelism, next) {
var start = new Date().getTime(); var start = new Date().getTime();
async.times(parallelism, async.times(parallelism,
function(id, callback) { function(id, callback) {

1082
test/bench/perf.js Executable file → Normal file

File diff suppressed because it is too large Load Diff

2
test/bench/random.js Executable file → Normal file
View File

@@ -8,6 +8,8 @@ var Benchmark = require('benchmark');
var sharp = require('../../index'); var sharp = require('../../index');
var fixtures = require('../fixtures'); var fixtures = require('../fixtures');
sharp.simd(true);
var min = 320; var min = 320;
var max = 960; var max = 960;

BIN
test/fixtures/Landscape_1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
test/fixtures/Landscape_2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_3.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
test/fixtures/Landscape_4.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
test/fixtures/Landscape_6.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_7.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
test/fixtures/Portrait_1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
test/fixtures/Portrait_2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_3.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_4.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
test/fixtures/Portrait_5.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
test/fixtures/Portrait_6.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_7.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
test/fixtures/Portrait_8.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
test/fixtures/expected/embed-16bit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 B

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
test/fixtures/expected/negate-alpha.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
test/fixtures/expected/negate-trans.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
test/fixtures/expected/negate-trans.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
test/fixtures/expected/negate.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
test/fixtures/expected/negate.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
test/fixtures/expected/threshold-1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
test/fixtures/expected/threshold-128.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
test/fixtures/expected/threshold-40.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

19
test/fixtures/index.js vendored Executable file → Normal file
View File

@@ -40,6 +40,24 @@ var fingerprint = function(image, callback) {
module.exports = { module.exports = {
inputJpgWithLandscapeExif1: getPath('Landscape_1.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif2: getPath('Landscape_2.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif3: getPath('Landscape_3.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif4: getPath('Landscape_4.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif5: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif6: getPath('Landscape_6.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif7: getPath('Landscape_7.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif8: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif1: getPath('Portrait_1.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif2: getPath('Portrait_2.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif3: getPath('Portrait_3.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif4: getPath('Portrait_4.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif5: getPath('Portrait_5.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif6: getPath('Portrait_6.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif7: getPath('Portrait_7.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif8: getPath('Portrait_8.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpg: getPath('2569067123_aca715a2ee_o.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/ inputJpg: getPath('2569067123_aca715a2ee_o.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/
inputJpgWithExif: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_8.jpg inputJpgWithExif: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_8.jpg
inputJpgWithExifMirroring: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_5.jpg inputJpgWithExifMirroring: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_5.jpg
@@ -53,6 +71,7 @@ module.exports = {
inputPngWithTransparency: getPath('blackbug.png'), // public domain inputPngWithTransparency: getPath('blackbug.png'), // public domain
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'), inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
inputPngWithOneColor: getPath('2x2_fdcce6.png'), inputPngWithOneColor: getPath('2x2_fdcce6.png'),
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'), inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'),
inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'), inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'),
inputPngOverlayLayer2: getPath('alpha-layer-2-ink.png'), inputPngOverlayLayer2: getPath('alpha-layer-2-ink.png'),

BIN
test/fixtures/tbgn2c16.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

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

Some files were not shown because too many files have changed in this diff Show More