Switch from libvips' C to C++ binding

Requires upgrade to libvips 8.2.1
This commit is contained in:
Lovell Fuller 2016-01-14 18:40:59 +00:00
parent 11329d5e09
commit 5c9c17f1f6
21 changed files with 4348 additions and 1178 deletions

View File

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

2
package.json Normal file → Executable file
View File

@ -68,7 +68,7 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"config": { "config": {
"libvips": "8.2.0" "libvips": "8.2.1"
}, },
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"

View File

@ -28,7 +28,7 @@ VERSION_WEBP=0.5.0
VERSION_TIFF=4.0.6 VERSION_TIFF=4.0.6
VERSION_MAGICK=6.9.2-10 VERSION_MAGICK=6.9.2-10
VERSION_ORC=0.4.24 VERSION_ORC=0.4.24
VERSION_VIPS=8.2.0 VERSION_VIPS=8.2.1
mkdir ${DEPS}/zlib mkdir ${DEPS}/zlib
curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1 curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
@ -106,11 +106,11 @@ cd ${DEPS}/vips
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \ --with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
&& make install-strip && make install-strip
# Remove the C++ bindings # Remove the old C++ bindings
cd ${TARGET}/include cd ${TARGET}/include
rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h rm -rf vips/vipsc++.h vips/vipscpp.h
cd ${TARGET}/lib cd ${TARGET}/lib
rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.* rm -rf pkgconfig .libs *.la libvipsCC*
# Create JSON file of version numbers # Create JSON file of version numbers
cd ${TARGET} cd ${TARGET}

View File

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

View File

@ -29,7 +29,7 @@ ENV VERSION_ZLIB=1.2.8 \
VERSION_TIFF=4.0.6 \ VERSION_TIFF=4.0.6 \
VERSION_MAGICK=6.9.2-10 \ VERSION_MAGICK=6.9.2-10 \
VERSION_ORC=0.4.24 \ VERSION_ORC=0.4.24 \
VERSION_VIPS=8.2.0 VERSION_VIPS=8.2.1
RUN mkdir ${DEPS}/zlib RUN mkdir ${DEPS}/zlib
RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1 RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
@ -107,11 +107,11 @@ RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-de
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \ --with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
&& make install-strip && make install-strip
# Remove the C++ bindings # Remove the old C++ bindings
WORKDIR ${TARGET}/include WORKDIR ${TARGET}/include
RUN rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h RUN rm -rf vips/vipsc++.h vips/vipscpp.h
WORKDIR ${TARGET}/lib WORKDIR ${TARGET}/lib
RUN rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.* RUN rm -rf pkgconfig .libs *.la libvipsCC*
# Create JSON file of version numbers # Create JSON file of version numbers
WORKDIR ${TARGET} WORKDIR ${TARGET}

View File

@ -6,12 +6,18 @@ RUN apt-get update && apt-get install -y curl zip
# Fetch and unzip # Fetch and unzip
RUN mkdir /vips RUN mkdir /vips
WORKDIR /vips WORKDIR /vips
RUN curl -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.zip RUN curl -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.1.zip
RUN unzip vips-dev-w64-8.2.zip RUN unzip vips-dev-w64-8.2.1.zip
# Clean and zip # Clean and zip
WORKDIR /vips/vips-dev-8.2 WORKDIR /vips/vips-dev-8.2
RUN rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll bin/libstdc++-6.dll RUN rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll bin/libstdc++-6.dll
RUN cp bin/*.dll lib/ RUN cp bin/*.dll lib/
RUN cp -r lib64/* lib/ RUN cp -r lib64/* lib/
RUN GZIP=-9 tar czf /libvips-8.2.0-win.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll
# Temporary workaround until libvips 8.2.2 is released
RUN curl -L -o include/vips/VError8.h https://raw.githubusercontent.com/jcupitt/libvips/master/cplusplus/include/vips/VError8.h
RUN curl -L -o include/vips/VImage8.h https://raw.githubusercontent.com/jcupitt/libvips/master/cplusplus/include/vips/VImage8.h
RUN curl -L -o include/vips/vips8 https://raw.githubusercontent.com/jcupitt/libvips/master/cplusplus/include/vips/vips8
RUN GZIP=-9 tar czf /libvips-8.2.1-win.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll

View File

@ -1,14 +1,14 @@
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <vips/vips.h> #include <vips/vips8>
#include "common.h" #include "common.h"
// Verify platform and compiler compatibility // Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 1 && VIPS_PATCH_VERSION < 1)) #if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 2))
#error libvips version 8.1.1+ required - see http://sharp.dimens.io/page/install #error libvips version 8.2.0+ required - see http://sharp.dimens.io/page/install
#endif #endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
@ -23,6 +23,8 @@
#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation" #define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
using vips::VImage;
namespace sharp { namespace sharp {
// How many tasks are in the queue? // How many tasks are in the queue?
@ -58,7 +60,7 @@ namespace sharp {
ImageType imageType = ImageType::UNKNOWN; ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load_buffer(buffer, length); char const *load = vips_foreign_find_load_buffer(buffer, length);
if (load != NULL) { if (load != NULL) {
std::string loader = load; std::string const loader = load;
if (EndsWith(loader, "JpegBuffer")) { if (EndsWith(loader, "JpegBuffer")) {
imageType = ImageType::JPEG; imageType = ImageType::JPEG;
} else if (EndsWith(loader, "PngBuffer")) { } else if (EndsWith(loader, "PngBuffer")) {
@ -74,13 +76,6 @@ namespace sharp {
return imageType; return imageType;
} }
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access) {
return vips_image_new_from_buffer(buffer, length, nullptr, "access", access, nullptr);
}
/* /*
Determine image format, reads the first few bytes of the file Determine image format, reads the first few bytes of the file
*/ */
@ -88,7 +83,7 @@ namespace sharp {
ImageType imageType = ImageType::UNKNOWN; ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load(file); char const *load = vips_foreign_find_load(file);
if (load != nullptr) { if (load != nullptr) {
std::string loader = load; std::string const loader = load;
if (EndsWith(loader, "JpegFile")) { if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG; imageType = ImageType::JPEG;
} else if (EndsWith(loader, "Png")) { } else if (EndsWith(loader, "Png")) {
@ -106,77 +101,57 @@ namespace sharp {
return imageType; return imageType;
} }
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(char const *file, VipsAccess const access) {
return vips_image_new_from_file(file, "access", access, nullptr);
}
/* /*
Does this image have an embedded profile? Does this image have an embedded profile?
*/ */
bool HasProfile(VipsImage *image) { bool HasProfile(VImage image) {
return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE; return (image.get_typeof(VIPS_META_ICC_NAME) != 0) ? TRUE : FALSE;
} }
/* /*
Does this image have an alpha channel? Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this. Uses colour space interpretation with number of channels to guess this.
*/ */
bool HasAlpha(VipsImage *image) { bool HasAlpha(VImage image) {
int const bands = image.bands();
VipsInterpretation const interpretation = image.interpretation();
return ( return (
(image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) || (bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) || (bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK) (bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK)
); );
} }
/* /*
Get EXIF Orientation of image, if any. Get EXIF Orientation of image, if any.
*/ */
int ExifOrientation(VipsImage const *image) { int ExifOrientation(VImage image) {
int orientation = 0; int orientation = 0;
const char *exif; if (image.get_typeof(EXIF_IFD0_ORIENTATION) != 0) {
if ( char const *exif = image.get_string(EXIF_IFD0_ORIENTATION);
vips_image_get_typeof(image, EXIF_IFD0_ORIENTATION) != 0 && if (exif != nullptr) {
!vips_image_get_string(image, EXIF_IFD0_ORIENTATION, &exif)
) {
orientation = atoi(&exif[0]); orientation = atoi(&exif[0]);
} }
}
return orientation; return orientation;
} }
/* /*
Set EXIF Orientation of image. Set EXIF Orientation of image.
*/ */
void SetExifOrientation(VipsImage *image, int const orientation) { void SetExifOrientation(VImage image, int const orientation) {
char exif[3]; char exif[3];
g_snprintf(exif, sizeof(exif), "%d", orientation); g_snprintf(exif, sizeof(exif), "%d", orientation);
vips_image_set_string(image, EXIF_IFD0_ORIENTATION, exif); image.set(EXIF_IFD0_ORIENTATION, exif);
} }
/* /*
Remove EXIF Orientation from image. Remove EXIF Orientation from image.
*/ */
void RemoveExifOrientation(VipsImage *image) { void RemoveExifOrientation(VImage image) {
SetExifOrientation(image, 0); SetExifOrientation(image, 0);
} }
/*
Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation.
*/
int InterpolatorWindowSize(char const *name) {
VipsInterpolate *interpolator = vips_interpolate_new(name);
if (interpolator == nullptr) {
return -1;
}
int window_size = vips_interpolate_get_window_size(interpolator);
g_object_unref(interpolator);
return window_size;
}
/* /*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/ */

View File

@ -2,6 +2,9 @@
#define SRC_COMMON_H_ #define SRC_COMMON_H_
#include <string> #include <string>
#include <vips/vips8>
using vips::VImage;
namespace sharp { namespace sharp {
@ -38,47 +41,31 @@ namespace sharp {
*/ */
ImageType DetermineImageType(char const *file); ImageType DetermineImageType(char const *file);
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access);
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(char const *file, VipsAccess const access);
/* /*
Does this image have an embedded profile? Does this image have an embedded profile?
*/ */
bool HasProfile(VipsImage *image); bool HasProfile(VImage image);
/* /*
Does this image have an alpha channel? Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this. Uses colour space interpretation with number of channels to guess this.
*/ */
bool HasAlpha(VipsImage *image); bool HasAlpha(VImage image);
/* /*
Get EXIF Orientation of image, if any. Get EXIF Orientation of image, if any.
*/ */
int ExifOrientation(VipsImage const *image); int ExifOrientation(VImage image);
/* /*
Set EXIF Orientation of image. Set EXIF Orientation of image.
*/ */
void SetExifOrientation(VipsImage *image, int const orientation); void SetExifOrientation(VImage image, int const orientation);
/* /*
Remove EXIF Orientation from image. Remove EXIF Orientation from image.
*/ */
void RemoveExifOrientation(VipsImage *image); void RemoveExifOrientation(VImage image);
/*
Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation.
*/
int InterpolatorWindowSize(char const *name);
/* /*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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

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

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
#include <node.h> #include <node.h>
#include <vips/vips.h> #include <vips/vips8>
#include "nan.h" #include "nan.h"

View File

@ -1,6 +1,6 @@
#include <cmath> #include <cmath>
#include <node.h> #include <node.h>
#include <vips/vips.h> #include <vips/vips8>
#include <vips/vector.h> #include <vips/vector.h>
#include "nan.h" #include "nan.h"
@ -196,103 +196,60 @@ NAN_METHOD(format) {
between two images of the same dimensions and number of channels. between two images of the same dimensions and number of channels.
*/ */
NAN_METHOD(_maxColourDistance) { NAN_METHOD(_maxColourDistance) {
using vips::VImage;
using vips::VError;
using sharp::DetermineImageType; using sharp::DetermineImageType;
using sharp::ImageType; using sharp::ImageType;
using sharp::InitImage;
using sharp::HasAlpha; using sharp::HasAlpha;
HandleScope(); HandleScope();
// Create "hook" VipsObject to hang image references from
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
// Open input files // Open input files
VipsImage *image1 = nullptr; VImage image1;
ImageType imageType1 = DetermineImageType(*Utf8String(info[0])); ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
if (imageType1 != ImageType::UNKNOWN) { if (imageType1 != ImageType::UNKNOWN) {
image1 = InitImage(*Utf8String(info[0]), VIPS_ACCESS_SEQUENTIAL); try {
if (image1 == nullptr) { image1 = VImage::new_from_file(*Utf8String(info[0]));
g_object_unref(hook); } catch (...) {
return ThrowError("Input file 1 has corrupt header"); return ThrowError("Input file 1 has corrupt header");
} else {
vips_object_local(hook, image1);
} }
} else { } else {
g_object_unref(hook);
return ThrowError("Input file 1 is of an unsupported image format"); return ThrowError("Input file 1 is of an unsupported image format");
} }
VipsImage *image2 = nullptr; VImage image2;
ImageType imageType2 = DetermineImageType(*Utf8String(info[1])); ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
if (imageType2 != ImageType::UNKNOWN) { if (imageType2 != ImageType::UNKNOWN) {
image2 = InitImage(*Utf8String(info[1]), VIPS_ACCESS_SEQUENTIAL); try {
if (image2 == nullptr) { image2 = VImage::new_from_file(*Utf8String(info[1]));
g_object_unref(hook); } catch (...) {
return ThrowError("Input file 2 has corrupt header"); return ThrowError("Input file 2 has corrupt header");
} else {
vips_object_local(hook, image2);
} }
} else { } else {
g_object_unref(hook);
return ThrowError("Input file 2 is of an unsupported image format"); return ThrowError("Input file 2 is of an unsupported image format");
} }
// Ensure same number of channels // Ensure same number of channels
if (image1->Bands != image2->Bands) { if (image1.bands() != image2.bands()) {
g_object_unref(hook);
return ThrowError("mismatchedBands"); return ThrowError("mismatchedBands");
} }
// Ensure same dimensions // Ensure same dimensions
if (image1->Xsize != image2->Xsize || image1->Ysize != image2->Ysize) { if (image1.width() != image2.width() || image1.height() != image2.height()) {
g_object_unref(hook);
return ThrowError("mismatchedDimensions"); return ThrowError("mismatchedDimensions");
} }
double maxColourDistance;
try {
// Premultiply and remove alpha // Premultiply and remove alpha
if (HasAlpha(image1)) { if (HasAlpha(image1)) {
VipsImage *imagePremultiplied1; image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
if (vips_premultiply(image1, &imagePremultiplied1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultiplied1);
VipsImage *imagePremultipliedNoAlpha1;
if (vips_extract_band(image1, &imagePremultipliedNoAlpha1, 1, "n", image1->Bands - 1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultipliedNoAlpha1);
image1 = imagePremultipliedNoAlpha1;
} }
if (HasAlpha(image2)) { if (HasAlpha(image2)) {
VipsImage *imagePremultiplied2; image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1));
if (vips_premultiply(image2, &imagePremultiplied2, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
} }
vips_object_local(hook, imagePremultiplied2);
VipsImage *imagePremultipliedNoAlpha2;
if (vips_extract_band(image2, &imagePremultipliedNoAlpha2, 1, "n", image2->Bands - 1, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultipliedNoAlpha2);
image2 = imagePremultipliedNoAlpha2;
}
// Calculate colour distance // Calculate colour distance
VipsImage *difference; maxColourDistance = image1.dE00(image2).max();
if (vips_dE00(image1, image2, &difference, nullptr)) { } catch (VError err) {
g_object_unref(hook); return ThrowError(err.what());
return ThrowError(vips_error_buffer());
} }
vips_object_local(hook, difference);
// Extract maximum distance
double maxColourDistance;
if (vips_max(difference, &maxColourDistance, nullptr)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
g_object_unref(hook);
// Clean up libvips' per-request data and threads // Clean up libvips' per-request data and threads
vips_error_clear(); vips_error_clear();

View File

@ -92,7 +92,6 @@ describe('Alpha transparency', function() {
var expected = fixtures.expected(BASE_NAME); var expected = fixtures.expected(BASE_NAME);
sharp(fixtures.inputPngAlphaPremultiplicationSmall) sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.resize(2048, 1536) .resize(2048, 1536)
.interpolateWith('bicubic')
.toFile(actual, function(err) { .toFile(actual, function(err) {
if (err) { if (err) {
done(err); done(err);
@ -109,7 +108,6 @@ describe('Alpha transparency', function() {
var expected = fixtures.expected(BASE_NAME); var expected = fixtures.expected(BASE_NAME);
sharp(fixtures.inputPngAlphaPremultiplicationLarge) sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(1024, 768) .resize(1024, 768)
.interpolateWith('bicubic')
.toFile(actual, function(err) { .toFile(actual, function(err) {
if (err) { if (err) {
done(err); done(err);

View File

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

View File

@ -640,7 +640,7 @@ describe('Input/output', function() {
.toFormat('png') .toFormat('png')
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
if (err) { if (err) {
assert.strictEqual(0, err.message.indexOf('Input file is of an unsupported image format')); assert.strictEqual(0, err.message.indexOf('Input file is missing or of an unsupported image format'));
done(); done();
} else { } else {
assert.strictEqual(true, info.size > 0); assert.strictEqual(true, info.size > 0);