From 5c9c17f1f6339a2a28005c314eaba44401767ffa Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Thu, 14 Jan 2016 18:40:59 +0000 Subject: [PATCH] Switch from libvips' C to C++ binding Requires upgrade to libvips 8.2.1 --- binding.gyp | 68 +- package.json | 2 +- packaging/arm/build.sh | 8 +- packaging/build.sh | 4 +- packaging/lin/Dockerfile | 8 +- packaging/win/Dockerfile | 12 +- src/common.cc | 73 +- src/common.h | 29 +- src/libvips/cplusplus/VError.cpp | 52 + src/libvips/cplusplus/VImage.cpp | 714 ++++++ src/libvips/cplusplus/VInterpolate.cpp | 76 + src/libvips/cplusplus/vips-operators.cpp | 2738 ++++++++++++++++++++++ src/metadata.cc | 56 +- src/operations.cc | 227 +- src/operations.h | 18 +- src/pipeline.cc | 1338 +++++------ src/sharp.cc | 2 +- src/utilities.cc | 93 +- test/unit/alpha.js | 2 - test/unit/cpplint.js | 4 +- test/unit/io.js | 2 +- 21 files changed, 4348 insertions(+), 1178 deletions(-) mode change 100644 => 100755 package.json create mode 100644 src/libvips/cplusplus/VError.cpp create mode 100644 src/libvips/cplusplus/VImage.cpp create mode 100644 src/libvips/cplusplus/VInterpolate.cpp create mode 100644 src/libvips/cplusplus/vips-operators.cpp mode change 100755 => 100644 src/operations.h diff --git a/binding.gyp b/binding.gyp index e44fe495..ad578ee4 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,6 +1,53 @@ { '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': '/dev/null || true)' + 'global_vips_version': '/dev/null || true)' }, { 'global_vips_version': '' }] @@ -43,18 +90,21 @@ 'src/sharp.cc', 'src/utilities.cc' ], + 'defines': [ + '_GLIBCXX_USE_CXX11_ABI=0' + ], 'include_dirs': [ '&1 || true)" node -e "require(\'./binding\').download_vips()")' }, 'libraries': [ + '<(module_root_dir)/lib/libvips-cpp.so', '<(module_root_dir)/lib/libvips.so', '<(module_root_dir)/lib/libglib-2.0.so', '<(module_root_dir)/lib/libgobject-2.0.so', @@ -118,6 +166,7 @@ 'CLANG_CXX_LANGUAGE_STANDARD': 'c++11', 'CLANG_CXX_LIBRARY': 'libc++', 'MACOSX_DEPLOYMENT_TARGET': '10.7', + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'OTHER_CPLUSPLUSFLAGS': [ '-fexceptions', '-Wall', @@ -130,7 +179,10 @@ 'VCCLCompilerTool': { 'ExceptionHandling': 1 } - } + }, + 'msvs_disabled_warnings': [ + 4275 + ] } }, }, { diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 248db5c2..91fef593 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ }, "license": "Apache-2.0", "config": { - "libvips": "8.2.0" + "libvips": "8.2.1" }, "engines": { "node": ">=0.10" diff --git a/packaging/arm/build.sh b/packaging/arm/build.sh index eeacf3ca..e9932099 100755 --- a/packaging/arm/build.sh +++ b/packaging/arm/build.sh @@ -28,7 +28,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 +VERSION_VIPS=8.2.1 mkdir ${DEPS}/zlib 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 \ && make install-strip -# Remove the C++ bindings +# Remove the old C++ bindings 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 -rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.* +rm -rf pkgconfig .libs *.la libvipsCC* # Create JSON file of version numbers cd ${TARGET} diff --git a/packaging/build.sh b/packaging/build.sh index 66ff1250..fd0ce6b3 100755 --- a/packaging/build.sh +++ b/packaging/build.sh @@ -13,14 +13,14 @@ fi 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 cp $WIN_CONTAINER_ID:/libvips-8.2.1-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 cp $LIN_CONTAINER_ID:/libvips-8.2.1-lin.tar.gz . docker rm $LIN_CONTAINER_ID # Checksums diff --git a/packaging/lin/Dockerfile b/packaging/lin/Dockerfile index c5e24743..62b7afeb 100644 --- a/packaging/lin/Dockerfile +++ b/packaging/lin/Dockerfile @@ -29,7 +29,7 @@ ENV VERSION_ZLIB=1.2.8 \ VERSION_TIFF=4.0.6 \ VERSION_MAGICK=6.9.2-10 \ VERSION_ORC=0.4.24 \ - VERSION_VIPS=8.2.0 + VERSION_VIPS=8.2.1 RUN mkdir ${DEPS}/zlib 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 \ && make install-strip -# Remove the C++ bindings +# Remove the old C++ bindings 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 -RUN rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.* +RUN rm -rf pkgconfig .libs *.la libvipsCC* # Create JSON file of version numbers WORKDIR ${TARGET} diff --git a/packaging/win/Dockerfile b/packaging/win/Dockerfile index d3d1d36e..d469c261 100644 --- a/packaging/win/Dockerfile +++ b/packaging/win/Dockerfile @@ -6,12 +6,18 @@ 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 +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.1.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 + +# 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 diff --git a/src/common.cc b/src/common.cc index 116c7744..5b7d2a58 100644 --- a/src/common.cc +++ b/src/common.cc @@ -1,14 +1,14 @@ #include #include #include -#include +#include #include "common.h" // Verify platform and compiler compatibility -#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 1 && VIPS_PATCH_VERSION < 1)) -#error libvips version 8.1.1+ required - see http://sharp.dimens.io/page/install +#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 2)) +#error libvips version 8.2.0+ required - see http://sharp.dimens.io/page/install #endif #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) @@ -23,6 +23,8 @@ #define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation" +using vips::VImage; + namespace sharp { // How many tasks are in the queue? @@ -58,7 +60,7 @@ namespace sharp { ImageType imageType = ImageType::UNKNOWN; char const *load = vips_foreign_find_load_buffer(buffer, length); if (load != NULL) { - std::string loader = load; + std::string const loader = load; if (EndsWith(loader, "JpegBuffer")) { imageType = ImageType::JPEG; } else if (EndsWith(loader, "PngBuffer")) { @@ -74,13 +76,6 @@ namespace sharp { 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 */ @@ -88,7 +83,7 @@ namespace sharp { ImageType imageType = ImageType::UNKNOWN; char const *load = vips_foreign_find_load(file); if (load != nullptr) { - std::string loader = load; + std::string const loader = load; if (EndsWith(loader, "JpegFile")) { imageType = ImageType::JPEG; } else if (EndsWith(loader, "Png")) { @@ -106,43 +101,37 @@ namespace sharp { 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? */ - bool HasProfile(VipsImage *image) { - return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE; + bool HasProfile(VImage image) { + return (image.get_typeof(VIPS_META_ICC_NAME) != 0) ? TRUE : FALSE; } /* Does this image have an alpha channel? 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 ( - (image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) || - (image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) || - (image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK) + (bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) || + (bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) || + (bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK) ); } /* Get EXIF Orientation of image, if any. */ - int ExifOrientation(VipsImage const *image) { + int ExifOrientation(VImage image) { int orientation = 0; - const char *exif; - if ( - vips_image_get_typeof(image, EXIF_IFD0_ORIENTATION) != 0 && - !vips_image_get_string(image, EXIF_IFD0_ORIENTATION, &exif) - ) { - orientation = atoi(&exif[0]); + if (image.get_typeof(EXIF_IFD0_ORIENTATION) != 0) { + char const *exif = image.get_string(EXIF_IFD0_ORIENTATION); + if (exif != nullptr) { + orientation = atoi(&exif[0]); + } } return orientation; } @@ -150,33 +139,19 @@ namespace sharp { /* Set EXIF Orientation of image. */ - void SetExifOrientation(VipsImage *image, int const orientation) { + void SetExifOrientation(VImage image, int const orientation) { char exif[3]; 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. */ - void RemoveExifOrientation(VipsImage *image) { + void RemoveExifOrientation(VImage image) { 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 */ diff --git a/src/common.h b/src/common.h index 88e081d2..e3801a7d 100644 --- a/src/common.h +++ b/src/common.h @@ -2,6 +2,9 @@ #define SRC_COMMON_H_ #include +#include + +using vips::VImage; namespace sharp { @@ -38,47 +41,31 @@ namespace sharp { */ 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? */ - bool HasProfile(VipsImage *image); + bool HasProfile(VImage image); /* Does this image have an alpha channel? 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. */ - int ExifOrientation(VipsImage const *image); + int ExifOrientation(VImage 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. */ - void RemoveExifOrientation(VipsImage *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); + void RemoveExifOrientation(VImage image); /* Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows diff --git a/src/libvips/cplusplus/VError.cpp b/src/libvips/cplusplus/VError.cpp new file mode 100644 index 00000000..67e67348 --- /dev/null +++ b/src/libvips/cplusplus/VError.cpp @@ -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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +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 diff --git a/src/libvips/cplusplus/VImage.cpp b/src/libvips/cplusplus/VImage.cpp new file mode 100644 index 00000000..26c06728 --- /dev/null +++ b/src/libvips/cplusplus/VImage.cpp @@ -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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +/* +#define VIPS_DEBUG +#define VIPS_DEBUG_VERBOSE + */ + +VIPS_NAMESPACE_START + +std::vector +to_vectorv( int n, ... ) +{ + std::vector 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 +to_vector( double value ) +{ + return( to_vectorv( 1, value ) ); +} + +std::vector +to_vector( int n, double array[] ) +{ + std::vector vector( n ); + + for( int i = 0; i < n; i++ ) + vector[i] = array[i]; + + return( vector ); +} + +std::vector +negate( std::vector vector ) +{ + std::vector new_vector( vector.size() ); + + for( unsigned int i = 0; i < vector.size(); i++ ) + new_vector[i] = vector[i] * -1; + + return( new_vector ); +} + +std::vector +invert( std::vector vector ) +{ + std::vector 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::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 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 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 *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::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::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 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::bandsplit( VOption *options ) +{ + std::vector 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 vec( v, v + VIPS_NUMBER( v ) ); + + return( bandjoin( vec, options ) ); +} + +std::complex +VImage::minpos( VOption *options ) +{ + double x, y; + + (void) min( + (options ? options : VImage::option()) -> + set( "x", &x ) -> + set( "y", &y ) ); + + return( std::complex( x, y ) ); +} + +std::complex +VImage::maxpos( VOption *options ) +{ + double x, y; + + (void) max( + (options ? options : VImage::option()) -> + set( "x", &x ) -> + set( "y", &y ) ); + + return( std::complex( x, y ) ); +} + +VIPS_NAMESPACE_END diff --git a/src/libvips/cplusplus/VInterpolate.cpp b/src/libvips/cplusplus/VInterpolate.cpp new file mode 100644 index 00000000..265bf66e --- /dev/null +++ b/src/libvips/cplusplus/VInterpolate.cpp @@ -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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +/* +#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 diff --git a/src/libvips/cplusplus/vips-operators.cpp b/src/libvips/cplusplus/vips-operators.cpp new file mode 100644 index 00000000..e6c0ba57 --- /dev/null +++ b/src/libvips/cplusplus/vips-operators.cpp @@ -0,0 +1,2738 @@ +// bodies for vips operations +// Sat Jan 9 15:05:58 GMT 2016 +// this file is generated automatically, do not edit! + +void VImage::system( char * cmd_format , VOption *options ) +{ + call( "system" , + (options ? options : VImage::option()) -> + set( "cmd-format", cmd_format ) ); +} + +VImage VImage::add( VImage right , VOption *options ) +{ + VImage out; + + call( "add" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::subtract( VImage right , VOption *options ) +{ + VImage out; + + call( "subtract" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::multiply( VImage right , VOption *options ) +{ + VImage out; + + call( "multiply" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::divide( VImage right , VOption *options ) +{ + VImage out; + + call( "divide" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::relational( VImage right , VipsOperationRelational relational , VOption *options ) +{ + VImage out; + + call( "relational" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) -> + set( "relational", relational ) ); + + return( out ); +} + +VImage VImage::remainder( VImage right , VOption *options ) +{ + VImage out; + + call( "remainder" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::boolean( VImage right , VipsOperationBoolean boolean , VOption *options ) +{ + VImage out; + + call( "boolean" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) -> + set( "boolean", boolean ) ); + + return( out ); +} + +VImage VImage::math2( VImage right , VipsOperationMath2 math2 , VOption *options ) +{ + VImage out; + + call( "math2" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) -> + set( "math2", math2 ) ); + + return( out ); +} + +VImage VImage::complex2( VImage right , VipsOperationComplex2 cmplx , VOption *options ) +{ + VImage out; + + call( "complex2" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) -> + set( "cmplx", cmplx ) ); + + return( out ); +} + +VImage VImage::complexform( VImage right , VOption *options ) +{ + VImage out; + + call( "complexform" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::sum( std::vector in , VOption *options ) +{ + VImage out; + + call( "sum" , + (options ? options : VImage::option()) -> + set( "in", in ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::invert( VOption *options ) +{ + VImage out; + + call( "invert" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::linear( std::vector a , std::vector b , VOption *options ) +{ + VImage out; + + call( "linear" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "a", a ) -> + set( "b", b ) ); + + return( out ); +} + +VImage VImage::math( VipsOperationMath math , VOption *options ) +{ + VImage out; + + call( "math" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "math", math ) ); + + return( out ); +} + +VImage VImage::abs( VOption *options ) +{ + VImage out; + + call( "abs" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::sign( VOption *options ) +{ + VImage out; + + call( "sign" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::round( VipsOperationRound round , VOption *options ) +{ + VImage out; + + call( "round" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "round", round ) ); + + return( out ); +} + +VImage VImage::relational_const( std::vector c , VipsOperationRelational relational , VOption *options ) +{ + VImage out; + + call( "relational_const" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "c", c ) -> + set( "relational", relational ) ); + + return( out ); +} + +VImage VImage::remainder_const( std::vector c , VOption *options ) +{ + VImage out; + + call( "remainder_const" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "c", c ) ); + + return( out ); +} + +VImage VImage::boolean_const( std::vector c , VipsOperationBoolean boolean , VOption *options ) +{ + VImage out; + + call( "boolean_const" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "c", c ) -> + set( "boolean", boolean ) ); + + return( out ); +} + +VImage VImage::math2_const( std::vector c , VipsOperationMath2 math2 , VOption *options ) +{ + VImage out; + + call( "math2_const" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "c", c ) -> + set( "math2", math2 ) ); + + return( out ); +} + +VImage VImage::complex( VipsOperationComplex cmplx , VOption *options ) +{ + VImage out; + + call( "complex" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "cmplx", cmplx ) ); + + return( out ); +} + +VImage VImage::complexget( VipsOperationComplexget get , VOption *options ) +{ + VImage out; + + call( "complexget" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "get", get ) ); + + return( out ); +} + +double VImage::avg( VOption *options ) +{ + double out; + + call( "avg" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +double VImage::min( VOption *options ) +{ + double out; + + call( "min" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +double VImage::max( VOption *options ) +{ + double out; + + call( "max" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +double VImage::deviate( VOption *options ) +{ + double out; + + call( "deviate" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::stats( VOption *options ) +{ + VImage out; + + call( "stats" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_find( VOption *options ) +{ + VImage out; + + call( "hist_find" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_find_ndim( VOption *options ) +{ + VImage out; + + call( "hist_find_ndim" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_find_indexed( VImage index , VOption *options ) +{ + VImage out; + + call( "hist_find_indexed" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "index", index ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hough_line( VOption *options ) +{ + VImage out; + + call( "hough_line" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hough_circle( VOption *options ) +{ + VImage out; + + call( "hough_circle" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::project( VImage * rows , VOption *options ) +{ + VImage columns; + + call( "project" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "columns", &columns ) -> + set( "rows", rows ) ); + + return( columns ); +} + +VImage VImage::profile( VImage * rows , VOption *options ) +{ + VImage columns; + + call( "profile" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "columns", &columns ) -> + set( "rows", rows ) ); + + return( columns ); +} + +VImage VImage::measure( int h , int v , VOption *options ) +{ + VImage out; + + call( "measure" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "h", h ) -> + set( "v", v ) ); + + return( out ); +} + +std::vector VImage::getpoint( int x , int y , VOption *options ) +{ + std::vector out_array; + + call( "getpoint" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out-array", &out_array ) -> + set( "x", x ) -> + set( "y", y ) ); + + return( out_array ); +} + +VImage VImage::copy( VOption *options ) +{ + VImage out; + + call( "copy" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::tilecache( VOption *options ) +{ + VImage out; + + call( "tilecache" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::linecache( VOption *options ) +{ + VImage out; + + call( "linecache" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::sequential( VOption *options ) +{ + VImage out; + + call( "sequential" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::cache( VOption *options ) +{ + VImage out; + + call( "cache" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::embed( int x , int y , int width , int height , VOption *options ) +{ + VImage out; + + call( "embed" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "x", x ) -> + set( "y", y ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::flip( VipsDirection direction , VOption *options ) +{ + VImage out; + + call( "flip" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) -> + set( "direction", direction ) ); + + return( out ); +} + +VImage VImage::insert( VImage sub , int x , int y , VOption *options ) +{ + VImage out; + + call( "insert" , + (options ? options : VImage::option()) -> + set( "main", *this ) -> + set( "sub", sub ) -> + set( "out", &out ) -> + set( "x", x ) -> + set( "y", y ) ); + + return( out ); +} + +VImage VImage::join( VImage in2 , VipsDirection direction , VOption *options ) +{ + VImage out; + + call( "join" , + (options ? options : VImage::option()) -> + set( "in1", *this ) -> + set( "in2", in2 ) -> + set( "out", &out ) -> + set( "direction", direction ) ); + + return( out ); +} + +VImage VImage::arrayjoin( std::vector in , VOption *options ) +{ + VImage out; + + call( "arrayjoin" , + (options ? options : VImage::option()) -> + set( "in", in ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::extract_area( int left , int top , int width , int height , VOption *options ) +{ + VImage out; + + call( "extract_area" , + (options ? options : VImage::option()) -> + set( "input", *this ) -> + set( "out", &out ) -> + set( "left", left ) -> + set( "top", top ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::extract_band( int band , VOption *options ) +{ + VImage out; + + call( "extract_band" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "band", band ) ); + + return( out ); +} + +VImage VImage::bandjoin( std::vector in , VOption *options ) +{ + VImage out; + + call( "bandjoin" , + (options ? options : VImage::option()) -> + set( "in", in ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::bandjoin_const( std::vector c , VOption *options ) +{ + VImage out; + + call( "bandjoin_const" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "c", c ) ); + + return( out ); +} + +VImage VImage::bandrank( std::vector in , VOption *options ) +{ + VImage out; + + call( "bandrank" , + (options ? options : VImage::option()) -> + set( "in", in ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::bandmean( VOption *options ) +{ + VImage out; + + call( "bandmean" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::bandbool( VipsOperationBoolean boolean , VOption *options ) +{ + VImage out; + + call( "bandbool" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "boolean", boolean ) ); + + return( out ); +} + +VImage VImage::replicate( int across , int down , VOption *options ) +{ + VImage out; + + call( "replicate" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "across", across ) -> + set( "down", down ) ); + + return( out ); +} + +VImage VImage::cast( VipsBandFormat format , VOption *options ) +{ + VImage out; + + call( "cast" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) -> + set( "format", format ) ); + + return( out ); +} + +VImage VImage::rot( VipsAngle angle , VOption *options ) +{ + VImage out; + + call( "rot" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) -> + set( "angle", angle ) ); + + return( out ); +} + +VImage VImage::rot45( VOption *options ) +{ + VImage out; + + call( "rot45" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::autorot( VOption *options ) +{ + VImage out; + + call( "autorot" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::ifthenelse( VImage in1 , VImage in2 , VOption *options ) +{ + VImage out; + + call( "ifthenelse" , + (options ? options : VImage::option()) -> + set( "cond", *this ) -> + set( "in1", in1 ) -> + set( "in2", in2 ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::recomb( VImage m , VOption *options ) +{ + VImage out; + + call( "recomb" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "m", m ) ); + + return( out ); +} + +VImage VImage::bandfold( VOption *options ) +{ + VImage out; + + call( "bandfold" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::bandunfold( VOption *options ) +{ + VImage out; + + call( "bandunfold" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::flatten( VOption *options ) +{ + VImage out; + + call( "flatten" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::premultiply( VOption *options ) +{ + VImage out; + + call( "premultiply" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::unpremultiply( VOption *options ) +{ + VImage out; + + call( "unpremultiply" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::grid( int tile_height , int across , int down , VOption *options ) +{ + VImage out; + + call( "grid" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) -> + set( "tile-height", tile_height ) -> + set( "across", across ) -> + set( "down", down ) ); + + return( out ); +} + +VImage VImage::scale( VOption *options ) +{ + VImage out; + + call( "scale" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::wrap( VOption *options ) +{ + VImage out; + + call( "wrap" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::zoom( int xfac , int yfac , VOption *options ) +{ + VImage out; + + call( "zoom" , + (options ? options : VImage::option()) -> + set( "input", *this ) -> + set( "out", &out ) -> + set( "xfac", xfac ) -> + set( "yfac", yfac ) ); + + return( out ); +} + +VImage VImage::subsample( int xfac , int yfac , VOption *options ) +{ + VImage out; + + call( "subsample" , + (options ? options : VImage::option()) -> + set( "input", *this ) -> + set( "out", &out ) -> + set( "xfac", xfac ) -> + set( "yfac", yfac ) ); + + return( out ); +} + +VImage VImage::msb( VOption *options ) +{ + VImage out; + + call( "msb" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::byteswap( VOption *options ) +{ + VImage out; + + call( "byteswap" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "in", *this ) ); + + return( out ); +} + +VImage VImage::falsecolour( VOption *options ) +{ + VImage out; + + call( "falsecolour" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::gamma( VOption *options ) +{ + VImage out; + + call( "gamma" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::black( int width , int height , VOption *options ) +{ + VImage out; + + call( "black" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::gaussnoise( int width , int height , VOption *options ) +{ + VImage out; + + call( "gaussnoise" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::text( char * text , VOption *options ) +{ + VImage out; + + call( "text" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "text", text ) ); + + return( out ); +} + +VImage VImage::xyz( int width , int height , VOption *options ) +{ + VImage out; + + call( "xyz" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::gaussmat( double sigma , double min_ampl , VOption *options ) +{ + VImage out; + + call( "gaussmat" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "sigma", sigma ) -> + set( "min-ampl", min_ampl ) ); + + return( out ); +} + +VImage VImage::logmat( double sigma , double min_ampl , VOption *options ) +{ + VImage out; + + call( "logmat" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "sigma", sigma ) -> + set( "min-ampl", min_ampl ) ); + + return( out ); +} + +VImage VImage::eye( int width , int height , VOption *options ) +{ + VImage out; + + call( "eye" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::grey( int width , int height , VOption *options ) +{ + VImage out; + + call( "grey" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::zone( int width , int height , VOption *options ) +{ + VImage out; + + call( "zone" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::sines( int width , int height , VOption *options ) +{ + VImage out; + + call( "sines" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::mask_ideal( int width , int height , double frequency_cutoff , VOption *options ) +{ + VImage out; + + call( "mask_ideal" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "frequency-cutoff", frequency_cutoff ) ); + + return( out ); +} + +VImage VImage::mask_ideal_ring( int width , int height , double frequency_cutoff , double ringwidth , VOption *options ) +{ + VImage out; + + call( "mask_ideal_ring" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "frequency-cutoff", frequency_cutoff ) -> + set( "ringwidth", ringwidth ) ); + + return( out ); +} + +VImage VImage::mask_ideal_band( int width , int height , double frequency_cutoff_x , double frequency_cutoff_y , double radius , VOption *options ) +{ + VImage out; + + call( "mask_ideal_band" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "frequency-cutoff-x", frequency_cutoff_x ) -> + set( "frequency-cutoff-y", frequency_cutoff_y ) -> + set( "radius", radius ) ); + + return( out ); +} + +VImage VImage::mask_butterworth( int width , int height , double order , double frequency_cutoff , double amplitude_cutoff , VOption *options ) +{ + VImage out; + + call( "mask_butterworth" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "order", order ) -> + set( "frequency-cutoff", frequency_cutoff ) -> + set( "amplitude-cutoff", amplitude_cutoff ) ); + + return( out ); +} + +VImage VImage::mask_butterworth_ring( int width , int height , double order , double frequency_cutoff , double amplitude_cutoff , double ringwidth , VOption *options ) +{ + VImage out; + + call( "mask_butterworth_ring" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "order", order ) -> + set( "frequency-cutoff", frequency_cutoff ) -> + set( "amplitude-cutoff", amplitude_cutoff ) -> + set( "ringwidth", ringwidth ) ); + + return( out ); +} + +VImage VImage::mask_butterworth_band( int width , int height , double order , double frequency_cutoff_x , double frequency_cutoff_y , double radius , double amplitude_cutoff , VOption *options ) +{ + VImage out; + + call( "mask_butterworth_band" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "order", order ) -> + set( "frequency-cutoff-x", frequency_cutoff_x ) -> + set( "frequency-cutoff-y", frequency_cutoff_y ) -> + set( "radius", radius ) -> + set( "amplitude-cutoff", amplitude_cutoff ) ); + + return( out ); +} + +VImage VImage::mask_gaussian( int width , int height , double frequency_cutoff , double amplitude_cutoff , VOption *options ) +{ + VImage out; + + call( "mask_gaussian" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "frequency-cutoff", frequency_cutoff ) -> + set( "amplitude-cutoff", amplitude_cutoff ) ); + + return( out ); +} + +VImage VImage::mask_gaussian_ring( int width , int height , double frequency_cutoff , double amplitude_cutoff , double ringwidth , VOption *options ) +{ + VImage out; + + call( "mask_gaussian_ring" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "frequency-cutoff", frequency_cutoff ) -> + set( "amplitude-cutoff", amplitude_cutoff ) -> + set( "ringwidth", ringwidth ) ); + + return( out ); +} + +VImage VImage::mask_gaussian_band( int width , int height , double frequency_cutoff_x , double frequency_cutoff_y , double radius , double amplitude_cutoff , VOption *options ) +{ + VImage out; + + call( "mask_gaussian_band" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "frequency-cutoff-x", frequency_cutoff_x ) -> + set( "frequency-cutoff-y", frequency_cutoff_y ) -> + set( "radius", radius ) -> + set( "amplitude-cutoff", amplitude_cutoff ) ); + + return( out ); +} + +VImage VImage::mask_fractal( int width , int height , double fractal_dimension , VOption *options ) +{ + VImage out; + + call( "mask_fractal" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "fractal-dimension", fractal_dimension ) ); + + return( out ); +} + +VImage VImage::buildlut( VOption *options ) +{ + VImage out; + + call( "buildlut" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::invertlut( VOption *options ) +{ + VImage out; + + call( "invertlut" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::tonelut( VOption *options ) +{ + VImage out; + + call( "tonelut" , + (options ? options : VImage::option()) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::identity( VOption *options ) +{ + VImage out; + + call( "identity" , + (options ? options : VImage::option()) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::fractsurf( int width , int height , double fractal_dimension , VOption *options ) +{ + VImage out; + + call( "fractsurf" , + (options ? options : VImage::option()) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "fractal-dimension", fractal_dimension ) ); + + return( out ); +} + +VImage VImage::radload( char * filename , VOption *options ) +{ + VImage out; + + call( "radload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::ppmload( char * filename , VOption *options ) +{ + VImage out; + + call( "ppmload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::csvload( char * filename , VOption *options ) +{ + VImage out; + + call( "csvload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::matrixload( char * filename , VOption *options ) +{ + VImage out; + + call( "matrixload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::analyzeload( char * filename , VOption *options ) +{ + VImage out; + + call( "analyzeload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::rawload( char * filename , int width , int height , int bands , VOption *options ) +{ + VImage out; + + call( "rawload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "bands", bands ) ); + + return( out ); +} + +VImage VImage::vipsload( char * filename , VOption *options ) +{ + VImage out; + + call( "vipsload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::pngload( char * filename , VOption *options ) +{ + VImage out; + + call( "pngload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::pngload_buffer( VipsBlob * buffer , VOption *options ) +{ + VImage out; + + call( "pngload_buffer" , + (options ? options : VImage::option()) -> + set( "buffer", buffer ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::matload( char * filename , VOption *options ) +{ + VImage out; + + call( "matload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::jpegload( char * filename , VOption *options ) +{ + VImage out; + + call( "jpegload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::jpegload_buffer( VipsBlob * buffer , VOption *options ) +{ + VImage out; + + call( "jpegload_buffer" , + (options ? options : VImage::option()) -> + set( "buffer", buffer ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::webpload( char * filename , VOption *options ) +{ + VImage out; + + call( "webpload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::webpload_buffer( VipsBlob * buffer , VOption *options ) +{ + VImage out; + + call( "webpload_buffer" , + (options ? options : VImage::option()) -> + set( "buffer", buffer ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::tiffload( char * filename , VOption *options ) +{ + VImage out; + + call( "tiffload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::tiffload_buffer( VipsBlob * buffer , VOption *options ) +{ + VImage out; + + call( "tiffload_buffer" , + (options ? options : VImage::option()) -> + set( "buffer", buffer ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::openslideload( char * filename , VOption *options ) +{ + VImage out; + + call( "openslideload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::magickload( char * filename , VOption *options ) +{ + VImage out; + + call( "magickload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::magickload_buffer( VipsBlob * buffer , VOption *options ) +{ + VImage out; + + call( "magickload_buffer" , + (options ? options : VImage::option()) -> + set( "buffer", buffer ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::fitsload( char * filename , VOption *options ) +{ + VImage out; + + call( "fitsload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::openexrload( char * filename , VOption *options ) +{ + VImage out; + + call( "openexrload" , + (options ? options : VImage::option()) -> + set( "filename", filename ) -> + set( "out", &out ) ); + + return( out ); +} + +void VImage::radsave( char * filename , VOption *options ) +{ + call( "radsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::ppmsave( char * filename , VOption *options ) +{ + call( "ppmsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::csvsave( char * filename , VOption *options ) +{ + call( "csvsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::matrixsave( char * filename , VOption *options ) +{ + call( "matrixsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::matrixprint( VOption *options ) +{ + call( "matrixprint" , + (options ? options : VImage::option()) -> + set( "in", *this ) ); +} + +void VImage::rawsave( char * filename , VOption *options ) +{ + call( "rawsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::rawsave_fd( int fd , VOption *options ) +{ + call( "rawsave_fd" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "fd", fd ) ); +} + +void VImage::vipssave( char * filename , VOption *options ) +{ + call( "vipssave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::dzsave( char * filename , VOption *options ) +{ + call( "dzsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::pngsave( char * filename , VOption *options ) +{ + call( "pngsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +VipsBlob * VImage::pngsave_buffer( VOption *options ) +{ + VipsBlob * buffer; + + call( "pngsave_buffer" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "buffer", &buffer ) ); + + return( buffer ); +} + +void VImage::jpegsave( char * filename , VOption *options ) +{ + call( "jpegsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +VipsBlob * VImage::jpegsave_buffer( VOption *options ) +{ + VipsBlob * buffer; + + call( "jpegsave_buffer" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "buffer", &buffer ) ); + + return( buffer ); +} + +void VImage::jpegsave_mime( VOption *options ) +{ + call( "jpegsave_mime" , + (options ? options : VImage::option()) -> + set( "in", *this ) ); +} + +void VImage::webpsave( char * filename , VOption *options ) +{ + call( "webpsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +VipsBlob * VImage::webpsave_buffer( VOption *options ) +{ + VipsBlob * buffer; + + call( "webpsave_buffer" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "buffer", &buffer ) ); + + return( buffer ); +} + +void VImage::tiffsave( char * filename , VOption *options ) +{ + call( "tiffsave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +void VImage::fitssave( char * filename , VOption *options ) +{ + call( "fitssave" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "filename", filename ) ); +} + +VImage VImage::mapim( VImage index , VOption *options ) +{ + VImage out; + + call( "mapim" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "index", index ) ); + + return( out ); +} + +VImage VImage::shrink( double xshrink , double yshrink , VOption *options ) +{ + VImage out; + + call( "shrink" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "xshrink", xshrink ) -> + set( "yshrink", yshrink ) ); + + return( out ); +} + +VImage VImage::shrinkh( int xshrink , VOption *options ) +{ + VImage out; + + call( "shrinkh" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "xshrink", xshrink ) ); + + return( out ); +} + +VImage VImage::shrinkv( int yshrink , VOption *options ) +{ + VImage out; + + call( "shrinkv" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "yshrink", yshrink ) ); + + return( out ); +} + +VImage VImage::shrink2( double xshrink , double yshrink , VOption *options ) +{ + VImage out; + + call( "shrink2" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "xshrink", xshrink ) -> + set( "yshrink", yshrink ) ); + + return( out ); +} + +VImage VImage::quadratic( VImage coeff , VOption *options ) +{ + VImage out; + + call( "quadratic" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "coeff", coeff ) ); + + return( out ); +} + +VImage VImage::affine( std::vector matrix , VOption *options ) +{ + VImage out; + + call( "affine" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "matrix", matrix ) ); + + return( out ); +} + +VImage VImage::similarity( VOption *options ) +{ + VImage out; + + call( "similarity" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::resize( double scale , VOption *options ) +{ + VImage out; + + call( "resize" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "scale", scale ) ); + + return( out ); +} + +VImage VImage::colourspace( VipsInterpretation space , VOption *options ) +{ + VImage out; + + call( "colourspace" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "space", space ) ); + + return( out ); +} + +VImage VImage::Lab2XYZ( VOption *options ) +{ + VImage out; + + call( "Lab2XYZ" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::XYZ2Lab( VOption *options ) +{ + VImage out; + + call( "XYZ2Lab" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::Lab2LCh( VOption *options ) +{ + VImage out; + + call( "Lab2LCh" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::LCh2Lab( VOption *options ) +{ + VImage out; + + call( "LCh2Lab" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::LCh2CMC( VOption *options ) +{ + VImage out; + + call( "LCh2CMC" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::CMC2LCh( VOption *options ) +{ + VImage out; + + call( "CMC2LCh" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::XYZ2Yxy( VOption *options ) +{ + VImage out; + + call( "XYZ2Yxy" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::Yxy2XYZ( VOption *options ) +{ + VImage out; + + call( "Yxy2XYZ" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::scRGB2XYZ( VOption *options ) +{ + VImage out; + + call( "scRGB2XYZ" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::XYZ2scRGB( VOption *options ) +{ + VImage out; + + call( "XYZ2scRGB" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::LabQ2Lab( VOption *options ) +{ + VImage out; + + call( "LabQ2Lab" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::Lab2LabQ( VOption *options ) +{ + VImage out; + + call( "Lab2LabQ" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::LabQ2LabS( VOption *options ) +{ + VImage out; + + call( "LabQ2LabS" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::LabS2LabQ( VOption *options ) +{ + VImage out; + + call( "LabS2LabQ" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::LabS2Lab( VOption *options ) +{ + VImage out; + + call( "LabS2Lab" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::Lab2LabS( VOption *options ) +{ + VImage out; + + call( "Lab2LabS" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::rad2float( VOption *options ) +{ + VImage out; + + call( "rad2float" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::float2rad( VOption *options ) +{ + VImage out; + + call( "float2rad" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::LabQ2sRGB( VOption *options ) +{ + VImage out; + + call( "LabQ2sRGB" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::sRGB2HSV( VOption *options ) +{ + VImage out; + + call( "sRGB2HSV" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::HSV2sRGB( VOption *options ) +{ + VImage out; + + call( "HSV2sRGB" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::icc_import( VOption *options ) +{ + VImage out; + + call( "icc_import" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::icc_export( VOption *options ) +{ + VImage out; + + call( "icc_export" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::icc_transform( char * output_profile , VOption *options ) +{ + VImage out; + + call( "icc_transform" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "output-profile", output_profile ) ); + + return( out ); +} + +VImage VImage::dE76( VImage right , VOption *options ) +{ + VImage out; + + call( "dE76" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::dE00( VImage right , VOption *options ) +{ + VImage out; + + call( "dE00" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::dECMC( VImage right , VOption *options ) +{ + VImage out; + + call( "dECMC" , + (options ? options : VImage::option()) -> + set( "left", *this ) -> + set( "right", right ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::sRGB2scRGB( VOption *options ) +{ + VImage out; + + call( "sRGB2scRGB" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::scRGB2BW( VOption *options ) +{ + VImage out; + + call( "scRGB2BW" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::scRGB2sRGB( VOption *options ) +{ + VImage out; + + call( "scRGB2sRGB" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::maplut( VImage lut , VOption *options ) +{ + VImage out; + + call( "maplut" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "lut", lut ) ); + + return( out ); +} + +int VImage::percent( double percent , VOption *options ) +{ + int threshold; + + call( "percent" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "percent", percent ) -> + set( "threshold", &threshold ) ); + + return( threshold ); +} + +VImage VImage::stdif( int width , int height , VOption *options ) +{ + VImage out; + + call( "stdif" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +VImage VImage::hist_cum( VOption *options ) +{ + VImage out; + + call( "hist_cum" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_match( VImage ref , VOption *options ) +{ + VImage out; + + call( "hist_match" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "ref", ref ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_norm( VOption *options ) +{ + VImage out; + + call( "hist_norm" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_equal( VOption *options ) +{ + VImage out; + + call( "hist_equal" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_plot( VOption *options ) +{ + VImage out; + + call( "hist_plot" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::hist_local( int width , int height , VOption *options ) +{ + VImage out; + + call( "hist_local" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) ); + + return( out ); +} + +bool VImage::hist_ismonotonic( VOption *options ) +{ + bool monotonic; + + call( "hist_ismonotonic" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "monotonic", &monotonic ) ); + + return( monotonic ); +} + +double VImage::hist_entropy( VOption *options ) +{ + double out; + + call( "hist_entropy" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::conv( VImage mask , VOption *options ) +{ + VImage out; + + call( "conv" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "mask", mask ) ); + + return( out ); +} + +VImage VImage::compass( VImage mask , VOption *options ) +{ + VImage out; + + call( "compass" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "mask", mask ) ); + + return( out ); +} + +VImage VImage::convsep( VImage mask , VOption *options ) +{ + VImage out; + + call( "convsep" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "mask", mask ) ); + + return( out ); +} + +VImage VImage::fastcor( VImage ref , VOption *options ) +{ + VImage out; + + call( "fastcor" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "ref", ref ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::spcor( VImage ref , VOption *options ) +{ + VImage out; + + call( "spcor" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "ref", ref ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::sharpen( VOption *options ) +{ + VImage out; + + call( "sharpen" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::gaussblur( double sigma , VOption *options ) +{ + VImage out; + + call( "gaussblur" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "sigma", sigma ) ); + + return( out ); +} + +VImage VImage::fwfft( VOption *options ) +{ + VImage out; + + call( "fwfft" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::invfft( VOption *options ) +{ + VImage out; + + call( "invfft" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::freqmult( VImage mask , VOption *options ) +{ + VImage out; + + call( "freqmult" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "mask", mask ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::spectrum( VOption *options ) +{ + VImage out; + + call( "spectrum" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::phasecor( VImage in2 , VOption *options ) +{ + VImage out; + + call( "phasecor" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "in2", in2 ) -> + set( "out", &out ) ); + + return( out ); +} + +VImage VImage::morph( VImage mask , VipsOperationMorphology morph , VOption *options ) +{ + VImage out; + + call( "morph" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "mask", mask ) -> + set( "morph", morph ) ); + + return( out ); +} + +VImage VImage::rank( int width , int height , int index , VOption *options ) +{ + VImage out; + + call( "rank" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "width", width ) -> + set( "height", height ) -> + set( "index", index ) ); + + return( out ); +} + +double VImage::countlines( VipsDirection direction , VOption *options ) +{ + double nolines; + + call( "countlines" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "nolines", &nolines ) -> + set( "direction", direction ) ); + + return( nolines ); +} + +VImage VImage::labelregions( VOption *options ) +{ + VImage mask; + + call( "labelregions" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "mask", &mask ) ); + + return( mask ); +} + +void VImage::draw_rect( std::vector ink , int left , int top , int width , int height , VOption *options ) +{ + call( "draw_rect" , + (options ? options : VImage::option()) -> + set( "image", *this ) -> + set( "ink", ink ) -> + set( "left", left ) -> + set( "top", top ) -> + set( "width", width ) -> + set( "height", height ) ); +} + +void VImage::draw_mask( std::vector ink , VImage mask , int x , int y , VOption *options ) +{ + call( "draw_mask" , + (options ? options : VImage::option()) -> + set( "image", *this ) -> + set( "ink", ink ) -> + set( "mask", mask ) -> + set( "x", x ) -> + set( "y", y ) ); +} + +void VImage::draw_line( std::vector ink , int x1 , int y1 , int x2 , int y2 , VOption *options ) +{ + call( "draw_line" , + (options ? options : VImage::option()) -> + set( "image", *this ) -> + set( "ink", ink ) -> + set( "x1", x1 ) -> + set( "y1", y1 ) -> + set( "x2", x2 ) -> + set( "y2", y2 ) ); +} + +void VImage::draw_circle( std::vector ink , int cx , int cy , int radius , VOption *options ) +{ + call( "draw_circle" , + (options ? options : VImage::option()) -> + set( "image", *this ) -> + set( "ink", ink ) -> + set( "cx", cx ) -> + set( "cy", cy ) -> + set( "radius", radius ) ); +} + +void VImage::draw_flood( std::vector ink , int x , int y , VOption *options ) +{ + call( "draw_flood" , + (options ? options : VImage::option()) -> + set( "image", *this ) -> + set( "ink", ink ) -> + set( "x", x ) -> + set( "y", y ) ); +} + +void VImage::draw_image( VImage sub , int x , int y , VOption *options ) +{ + call( "draw_image" , + (options ? options : VImage::option()) -> + set( "image", *this ) -> + set( "sub", sub ) -> + set( "x", x ) -> + set( "y", y ) ); +} + +void VImage::draw_smudge( int left , int top , int width , int height , VOption *options ) +{ + call( "draw_smudge" , + (options ? options : VImage::option()) -> + set( "image", *this ) -> + set( "left", left ) -> + set( "top", top ) -> + set( "width", width ) -> + set( "height", height ) ); +} + +VImage VImage::merge( VImage sec , VipsDirection direction , int dx , int dy , VOption *options ) +{ + VImage out; + + call( "merge" , + (options ? options : VImage::option()) -> + set( "ref", *this ) -> + set( "sec", sec ) -> + set( "out", &out ) -> + set( "direction", direction ) -> + set( "dx", dx ) -> + set( "dy", dy ) ); + + return( out ); +} + +VImage VImage::mosaic( VImage sec , VipsDirection direction , int xref , int yref , int xsec , int ysec , VOption *options ) +{ + VImage out; + + call( "mosaic" , + (options ? options : VImage::option()) -> + set( "ref", *this ) -> + set( "sec", sec ) -> + set( "out", &out ) -> + set( "direction", direction ) -> + set( "xref", xref ) -> + set( "yref", yref ) -> + set( "xsec", xsec ) -> + set( "ysec", ysec ) ); + + return( out ); +} + +VImage VImage::mosaic1( VImage sec , VipsDirection direction , int xr1 , int yr1 , int xs1 , int ys1 , int xr2 , int yr2 , int xs2 , int ys2 , VOption *options ) +{ + VImage out; + + call( "mosaic1" , + (options ? options : VImage::option()) -> + set( "ref", *this ) -> + set( "sec", sec ) -> + set( "out", &out ) -> + set( "direction", direction ) -> + set( "xr1", xr1 ) -> + set( "yr1", yr1 ) -> + set( "xs1", xs1 ) -> + set( "ys1", ys1 ) -> + set( "xr2", xr2 ) -> + set( "yr2", yr2 ) -> + set( "xs2", xs2 ) -> + set( "ys2", ys2 ) ); + + return( out ); +} + +VImage VImage::match( VImage sec , int xr1 , int yr1 , int xs1 , int ys1 , int xr2 , int yr2 , int xs2 , int ys2 , VOption *options ) +{ + VImage out; + + call( "match" , + (options ? options : VImage::option()) -> + set( "ref", *this ) -> + set( "sec", sec ) -> + set( "out", &out ) -> + set( "xr1", xr1 ) -> + set( "yr1", yr1 ) -> + set( "xs1", xs1 ) -> + set( "ys1", ys1 ) -> + set( "xr2", xr2 ) -> + set( "yr2", yr2 ) -> + set( "xs2", xs2 ) -> + set( "ys2", ys2 ) ); + + return( out ); +} + +VImage VImage::globalbalance( VOption *options ) +{ + VImage out; + + call( "globalbalance" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) ); + + return( out ); +} + diff --git a/src/metadata.cc b/src/metadata.cc index 7fab9b71..bcd933e2 100644 --- a/src/metadata.cc +++ b/src/metadata.cc @@ -1,5 +1,5 @@ #include -#include +#include #include "nan.h" @@ -29,9 +29,11 @@ using Nan::NewBuffer; using Nan::Null; using Nan::Error; +using vips::VImage; +using vips::VError; + using sharp::ImageType; using sharp::DetermineImageType; -using sharp::InitImage; using sharp::HasProfile; using sharp::HasAlpha; using sharp::ExifOrientation; @@ -81,13 +83,14 @@ class MetadataWorker : public AsyncWorker { g_atomic_int_dec_and_test(&counterQueue); ImageType imageType = ImageType::UNKNOWN; - VipsImage *image = nullptr; + VImage image; if (baton->bufferInLength > 0) { // From buffer imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength); if (imageType != ImageType::UNKNOWN) { - image = InitImage(baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM); - if (image == nullptr) { + try { + image = VImage::new_from_buffer(baton->bufferIn, baton->bufferInLength, nullptr); + } catch (...) { (baton->err).append("Input buffer has corrupt header"); imageType = ImageType::UNKNOWN; } @@ -98,16 +101,17 @@ class MetadataWorker : public AsyncWorker { // From file imageType = DetermineImageType(baton->fileIn.data()); if (imageType != ImageType::UNKNOWN) { - image = InitImage(baton->fileIn.data(), VIPS_ACCESS_RANDOM); - if (image == nullptr) { + try { + image = VImage::new_from_file(baton->fileIn.data()); + } catch (...) { (baton->err).append("Input file has corrupt header"); imageType = ImageType::UNKNOWN; } } 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 switch (imageType) { case ImageType::JPEG: baton->format = "jpeg"; break; @@ -119,36 +123,30 @@ class MetadataWorker : public AsyncWorker { case ImageType::UNKNOWN: break; } // VipsImage attributes - baton->width = image->Xsize; - baton->height = image->Ysize; - baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type); - baton->channels = image->Bands; + baton->width = image.width(); + baton->height = image.height(); + baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation()); + baton->channels = image.bands(); baton->hasProfile = HasProfile(image); // Derived attributes baton->hasAlpha = HasAlpha(image); baton->orientation = ExifOrientation(image); // EXIF - if (vips_image_get_typeof(image, VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) { - void* exif; + if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) { size_t exifLength; - if (!vips_image_get_blob(image, VIPS_META_EXIF_NAME, &exif, &exifLength)) { - baton->exifLength = exifLength; - baton->exif = static_cast(g_malloc(exifLength)); - memcpy(baton->exif, exif, exifLength); - } + void const *exif = image.get_blob(VIPS_META_EXIF_NAME, &exifLength); + baton->exif = static_cast(g_malloc(exifLength)); + memcpy(baton->exif, exif, exifLength); + baton->exifLength = exifLength; } // ICC profile - if (vips_image_get_typeof(image, VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) { - void* icc; + if (image.get_typeof(VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) { size_t iccLength; - if (!vips_image_get_blob(image, VIPS_META_ICC_NAME, &icc, &iccLength)) { - baton->iccLength = iccLength; - baton->icc = static_cast(g_malloc(iccLength)); - memcpy(baton->icc, icc, iccLength); - } + void const *icc = image.get_blob(VIPS_META_ICC_NAME, &iccLength); + baton->icc = static_cast(g_malloc(iccLength)); + memcpy(baton->icc, icc, iccLength); + baton->iccLength = iccLength; } - // Drop image reference - g_object_unref(image); } // Clean up vips_error_clear(); diff --git a/src/operations.cc b/src/operations.cc index b00c1463..1a6bb4f0 100644 --- a/src/operations.cc +++ b/src/operations.cc @@ -1,67 +1,38 @@ -#include +#include #include "common.h" #include "operations.h" +using vips::VImage; + namespace sharp { /* Alpha composite src over dst 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; // Split src into non-alpha and alpha - VipsImage *srcWithoutAlpha; - if (vips_extract_band(src, &srcWithoutAlpha, 0, "n", src->Bands - 1, nullptr)) - 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); + VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1)); + VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0); // Split dst into non-alpha and alpha channels - VipsImage *dstWithoutAlpha; - VipsImage *dstAlpha; + VImage dstWithoutAlpha; + VImage dstAlpha; if (HasAlpha(dst)) { // Non-alpha: extract all-but-last channel - if (vips_extract_band(dst, &dstWithoutAlpha, 0, "n", dst->Bands - 1, nullptr)) { - return -1; - } - vips_object_local(context, dstWithoutAlpha); + dstWithoutAlpha = dst.extract_band(0, VImage::option()->set("n", dst.bands() - 1)); // Alpha: Extract last channel - if (vips_extract_band(dst, &dstAlpha, dst->Bands - 1, "n", 1, nullptr)) { - return -1; - } - vips_object_local(context, dstAlpha); + dstAlpha = dst[dst.bands() - 1] * (1.0 / 255.0); } else { // Non-alpha: Copy reference dstWithoutAlpha = dst; // Alpha: Use blank, opaque (0xFF) image - VipsImage *black; - 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); + dstAlpha = VImage::black(dst.width(), dst.height()).invert(); } - // 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: // @@ -72,22 +43,8 @@ namespace sharp { // out_a = src_a + dst_a * (1 - src_a) // ^^^^^^^^^^^ // t0 - // ^^^^^^^^^^^^^^^^^^^ - // t1 - 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); + VImage t0 = srcAlpha.linear(-1.0, 1.0); + VImage outAlphaNormalized = srcAlpha + dstAlpha * t0; // // Compute output RGB channels: @@ -101,182 +58,86 @@ namespace sharp { // premultiplied RGBA image as reversal of premultiplication is handled // externally. // - VipsImage *t2; - 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); + VImage outRGBPremultiplied = srcWithoutAlpha + dstWithoutAlpha * t0; // 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. */ - int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) { + VImage Normalize(VImage image) { // Get original colourspace - VipsInterpretation typeBeforeNormalize = image->Type; + VipsInterpretation typeBeforeNormalize = image.interpretation(); if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) { typeBeforeNormalize = VIPS_INTERPRETATION_sRGB; } // Convert to LAB colourspace - VipsImage *lab; - if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, nullptr)) { - return -1; - } - vips_object_local(context, lab); + VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB); // Extract luminance - VipsImage *luminance; - 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); + VImage luminance = lab[0]; // Find luminance range - VipsImage *stats; - if (vips_stats(luminance, &stats, nullptr)) { - return -1; - } - vips_object_local(context, stats); - double min = *VIPS_MATRIX(stats, 0, 0); - double max = *VIPS_MATRIX(stats, 1, 0); + VImage stats = luminance.stats(); + double min = stats(0, 0)[0]; + double max = stats(1, 0)[0]; 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 a = -(min * f); - // Scale luminance - VipsImage *luminance100; - 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); + // Scale luminance, join to chroma, convert back to original colourspace + VImage normalized = luminance.linear(f, a).bandjoin(chroma).colourspace(typeBeforeNormalize); // Attach original alpha channel, if any if (HasAlpha(image)) { // Extract original alpha channel - VipsImage *alpha; - if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, nullptr)) { - return -1; - } - vips_object_local(context, alpha); + VImage alpha = image[image.bands() - 1]; // Join alpha channel to normalised image - VipsImage *normalizedAlpha; - if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, nullptr)) { - return -1; - } - vips_object_local(context, normalizedAlpha); - *out = normalizedAlpha; + return normalized.bandjoin(alpha); } 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) */ - int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma) { - VipsImage *blurred; + VImage Blur(VImage image, double const sigma) { + VImage blurred; if (sigma < 0.0) { // 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); - vips_image_set_double(blur, "scale", 9); - vips_object_local(context, blur); - if (vips_conv(image, &blurred, blur, nullptr)) { - return -1; - } + blur.set("scale", 9.0); + return image.conv(blur); } else { // Slower, accurate Gaussian blur - // Create Gaussian function for standard deviation - 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; - } + return image.gaussblur(sigma); } - vips_object_local(context, blurred); - *out = blurred; - return 0; } /* * 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) { - VipsImage *sharpened; + VImage Sharpen(VImage image, int const radius, double const flat, double const jagged) { if (radius == -1) { // 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, 32.0, -1.0, -1.0, -1.0, -1.0); - vips_image_set_double(sharpen, "scale", 24); - vips_object_local(context, sharpen); - if (vips_conv(image, &sharpened, sharpen, nullptr)) { - return -1; - } + sharpen.set("scale", 24.0); + return image.conv(sharpen); } else { // 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 -1; - } + return image.sharpen( + 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 diff --git a/src/operations.h b/src/operations.h old mode 100755 new mode 100644 index cabe4a2e..3ce63036 --- a/src/operations.h +++ b/src/operations.h @@ -1,34 +1,32 @@ #ifndef SRC_OPERATIONS_H_ #define SRC_OPERATIONS_H_ +#include + +using vips::VImage; + namespace sharp { /* Composite images `src` and `dst` with premultiplied alpha channel and output 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. */ - int Normalize(VipsObject *context, VipsImage *image, VipsImage **out); + VImage Normalize(VImage image); /* * 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. */ - 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); + VImage Sharpen(VImage image, int const radius, double const flat, double const jagged); } // namespace sharp #endif // SRC_OPERATIONS_H_ diff --git a/src/pipeline.cc b/src/pipeline.cc index 91a62662..02c812e0 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -1,9 +1,10 @@ #include #include +#include #include #include #include -#include +#include #include "nan.h" @@ -36,16 +37,17 @@ using Nan::NewBuffer; using Nan::Null; using Nan::Equals; +using vips::VImage; +using vips::VInterpolate; +using vips::VError; + using sharp::Composite; using sharp::Normalize; using sharp::Blur; using sharp::Sharpen; -using sharp::Threshold; using sharp::ImageType; using sharp::DetermineImageType; -using sharp::InitImage; -using sharp::InterpolatorWindowSize; using sharp::HasProfile; using sharp::HasAlpha; using sharp::ExifOrientation; @@ -68,14 +70,6 @@ enum class Canvas { IGNORE_ASPECT }; -enum class Angle { - D0, - D90, - D180, - D270, - DLAST -}; - struct PipelineBaton { std::string fileIn; char *bufferIn; @@ -197,19 +191,19 @@ class PipelineWorker : public AsyncWorker { // Latest v2 sRGB ICC profile std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc"; - // Create "hook" VipsObject to hang image references from - hook = reinterpret_cast(vips_image_new()); - // Input ImageType inputImageType = ImageType::UNKNOWN; - VipsImage *image = nullptr; + VImage image; if (baton->bufferInLength > 0) { // From buffer inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength); if (inputImageType != ImageType::UNKNOWN) { - image = InitImage(baton->bufferIn, baton->bufferInLength, baton->accessMethod); - if (image == nullptr) { - // Could not read header data + try { + image = VImage::new_from_buffer( + baton->bufferIn, baton->bufferInLength, nullptr, + VImage::option()->set("access", baton->accessMethod) + ); + } catch (...) { (baton->err).append("Input buffer has corrupt header"); inputImageType = ImageType::UNKNOWN; } @@ -220,803 +214,572 @@ class PipelineWorker : public AsyncWorker { // From file inputImageType = DetermineImageType(baton->fileIn.data()); if (inputImageType != ImageType::UNKNOWN) { - image = InitImage(baton->fileIn.data(), baton->accessMethod); - if (image == nullptr) { + try { + image = VImage::new_from_file( + baton->fileIn.data(), + VImage::option()->set("access", baton->accessMethod) + ); + } catch (...) { (baton->err).append("Input file has corrupt header"); inputImageType = ImageType::UNKNOWN; } } 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 || inputImageType == ImageType::UNKNOWN) { + if (inputImageType == ImageType::UNKNOWN) { return Error(); } - vips_object_local(hook, image); // Limit input images to a given number of pixels, where pixels = width * height - if (image->Xsize * image->Ysize > baton->limitInputPixels) { + if (image.width() * image.height() > baton->limitInputPixels) { (baton->err).append("Input image exceeds pixel limit"); return Error(); } - // Calculate angle of rotation - Angle rotation; - bool flip; - bool flop; - std::tie(rotation, flip, flop) = CalculateRotationAndFlip(baton->angle, image); - if (flip && !baton->flip) { - // Add flip operation due to EXIF mirroring - baton->flip = TRUE; - } - if (flop && !baton->flop) { - // Add flip operation due to EXIF mirroring - baton->flop = TRUE; - } - - // Rotate pre-extract - if (baton->rotateBeforePreExtract && rotation != Angle::D0) { - VipsImage *rotated; - if (vips_rot(image, &rotated, static_cast(rotation), nullptr)) { - return Error(); + try { + // Calculate angle of rotation + VipsAngle rotation; + bool flip; + bool flop; + std::tie(rotation, flip, flop) = CalculateRotationAndFlip(baton->angle, image); + if (flip && !baton->flip) { + // Add flip operation due to EXIF mirroring + baton->flip = TRUE; } - vips_object_local(hook, rotated); - image = rotated; - RemoveExifOrientation(image); - } - - // Pre extraction - if (baton->topOffsetPre != -1) { - VipsImage *extractedPre; - if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, nullptr)) { - return Error(); + if (flop && !baton->flop) { + // Add flip operation due to EXIF mirroring + baton->flop = TRUE; } - vips_object_local(hook, extractedPre); - image = extractedPre; - } - // Get pre-resize image width and height - int inputWidth = image->Xsize; - int inputHeight = image->Ysize; - if (!baton->rotateBeforePreExtract && (rotation == Angle::D90 || rotation == Angle::D270)) { - // Swap input output width and height when rotating by 90 or 270 degrees - int swap = inputWidth; - inputWidth = inputHeight; - inputHeight = swap; - } - - // Get window size of interpolator, used for determining shrink vs affine - int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.data()); - if (interpolatorWindowSize < 0) { - return Error(); - } - - // Scaling calculations - double xfactor = 1.0; - double yfactor = 1.0; - if (baton->width > 0 && baton->height > 0) { - // Fixed width and height - xfactor = static_cast(inputWidth) / static_cast(baton->width); - yfactor = static_cast(inputHeight) / static_cast(baton->height); - switch (baton->canvas) { - case Canvas::CROP: - xfactor = std::min(xfactor, yfactor); - yfactor = xfactor; - break; - case Canvas::EMBED: - xfactor = std::max(xfactor, yfactor); - yfactor = xfactor; - break; - case Canvas::MAX: - if (xfactor > yfactor) { - baton->height = static_cast(round(static_cast(inputHeight) / xfactor)); - yfactor = xfactor; - } else { - baton->width = static_cast(round(static_cast(inputWidth) / yfactor)); - xfactor = yfactor; - } - break; - case Canvas::MIN: - if (xfactor < yfactor) { - baton->height = static_cast(round(static_cast(inputHeight) / xfactor)); - yfactor = xfactor; - } else { - baton->width = static_cast(round(static_cast(inputWidth) / yfactor)); - xfactor = yfactor; - } - break; - case Canvas::IGNORE_ASPECT: - // xfactor, yfactor OK! - break; + // Rotate pre-extract + if (baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) { + image = image.rot(rotation); + RemoveExifOrientation(image); } - } else if (baton->width > 0) { - // Fixed width - xfactor = static_cast(inputWidth) / static_cast(baton->width); - if (baton->canvas == Canvas::IGNORE_ASPECT) { - baton->height = inputHeight; - } else { - // Auto height - yfactor = xfactor; - baton->height = static_cast(round(static_cast(inputHeight) / yfactor)); - } - } else if (baton->height > 0) { - // Fixed height - yfactor = static_cast(inputHeight) / static_cast(baton->height); - if (baton->canvas == Canvas::IGNORE_ASPECT) { - baton->width = inputWidth; - } else { - // Auto width - xfactor = yfactor; - baton->width = static_cast(round(static_cast(inputWidth) / xfactor)); - } - } else { - // Identity transform - baton->width = inputWidth; - baton->height = inputHeight; - } - // Calculate integral box shrink - int xshrink = CalculateShrink(xfactor, interpolatorWindowSize); - int yshrink = CalculateShrink(yfactor, interpolatorWindowSize); - - // Calculate residual float affine transformation - double xresidual = CalculateResidual(xshrink, xfactor); - double yresidual = CalculateResidual(yshrink, yfactor); - - // Do not enlarge the output if the input width *or* height are already less than the required dimensions - if (baton->withoutEnlargement) { - if (inputWidth < baton->width || inputHeight < baton->height) { - xfactor = 1; - yfactor = 1; - xshrink = 1; - yshrink = 1; - xresidual = 0; - yresidual = 0; - baton->width = inputWidth; - baton->height = inputHeight; + // Pre extraction + if (baton->topOffsetPre != -1) { + image = image.extract_area(baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre); } - } - // If integral x and y shrink are equal, try to use libjpeg shrink-on-load, but not when applying gamma correction or pre-resize extract - int shrink_on_load = 1; - if (xshrink == yshrink && inputImageType == ImageType::JPEG && xshrink >= 2 && baton->gamma == 0 && baton->topOffsetPre == -1) { - if (xshrink >= 8) { - xfactor = xfactor / 8; - yfactor = yfactor / 8; - shrink_on_load = 8; - } else if (xshrink >= 4) { - xfactor = xfactor / 4; - yfactor = yfactor / 4; - shrink_on_load = 4; - } else if (xshrink >= 2) { - xfactor = xfactor / 2; - yfactor = yfactor / 2; - shrink_on_load = 2; - } - } - if (shrink_on_load > 1) { - // Recalculate integral shrink and double residual - xfactor = std::max(xfactor, 1.0); - yfactor = std::max(yfactor, 1.0); - xshrink = CalculateShrink(xfactor, interpolatorWindowSize); - yshrink = CalculateShrink(yfactor, interpolatorWindowSize); - xresidual = CalculateResidual(xshrink, xfactor); - yresidual = CalculateResidual(yshrink, yfactor); - // Reload input using shrink-on-load - VipsImage *shrunkOnLoad; - if (baton->bufferInLength > 1) { - if (vips_jpegload_buffer(baton->bufferIn, baton->bufferInLength, &shrunkOnLoad, "shrink", shrink_on_load, nullptr)) { - return Error(); - } - } else { - if (vips_jpegload((baton->fileIn).data(), &shrunkOnLoad, "shrink", shrink_on_load, nullptr)) { - return Error(); - } - } - vips_object_local(hook, shrunkOnLoad); - image = shrunkOnLoad; - } - - // Ensure we're using a device-independent colour space - if (HasProfile(image)) { - // Convert to sRGB using embedded profile - VipsImage *transformed; - 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 - vips_object_local(hook, transformed); - image = transformed; - } - } else if (image->Type == VIPS_INTERPRETATION_CMYK) { - // Convert to sRGB using default "USWebCoatedSWOP" CMYK profile - std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc"; - VipsImage *transformed; - if ( - vips_icc_transform(image, &transformed, srgbProfile.data(), - "input_profile", cmykProfile.data(), "intent", VIPS_INTENT_PERCEPTUAL, nullptr) - ) { - return Error(); - } - vips_object_local(hook, 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 - 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 - VipsArrayDouble *background = vips_array_double_newv( - 3, // Ignore alpha channel as we're about to remove it - baton->background[0] * multiplier, - baton->background[1] * multiplier, - baton->background[2] * multiplier - ); - VipsImage *flattened; - if (vips_flatten(image, &flattened, "background", background, "max_alpha", maxAlpha, nullptr)) { - vips_area_unref(reinterpret_cast(background)); - return Error(); - } - vips_area_unref(reinterpret_cast(background)); - vips_object_local(hook, 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) - if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) { - VipsImage *gammaEncoded; - if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, nullptr)) { - return Error(); - } - vips_object_local(hook, gammaEncoded); - image = gammaEncoded; - } - - // Convert to greyscale (linear, therefore after gamma encoding, if any) - if (baton->greyscale) { - VipsImage *greyscale; - if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, nullptr)) { - return Error(); - } - vips_object_local(hook, greyscale); - image = greyscale; - } - - if (xshrink > 1 || yshrink > 1) { - VipsImage *shrunk; - // Use vips_shrink with the integral reduction - if (vips_shrink(image, &shrunk, xshrink, yshrink, nullptr)) { - return Error(); - } - vips_object_local(hook, shrunk); - image = shrunk; - // Recalculate residual float based on dimensions of required vs shrunk images - int shrunkWidth = shrunk->Xsize; - int shrunkHeight = shrunk->Ysize; - if (rotation == Angle::D90 || rotation == Angle::D270) { + // Get pre-resize image width and height + int inputWidth = image.width(); + int inputHeight = image.height(); + if (!baton->rotateBeforePreExtract && (rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)) { // Swap input output width and height when rotating by 90 or 270 degrees - int swap = shrunkWidth; - shrunkWidth = shrunkHeight; - shrunkHeight = swap; + std::swap(inputWidth, inputHeight); } - xresidual = static_cast(baton->width) / static_cast(shrunkWidth); - yresidual = static_cast(baton->height) / static_cast(shrunkHeight); - if (baton->canvas == Canvas::EMBED) { - xresidual = std::min(xresidual, yresidual); - yresidual = xresidual; - } else if (baton->canvas != Canvas::IGNORE_ASPECT) { - xresidual = std::max(xresidual, yresidual); - yresidual = xresidual; - } - } - bool shouldAffineTransform = xresidual != 0.0 || yresidual != 0.0; - bool shouldBlur = baton->blurSigma != 0.0; - bool shouldSharpen = baton->sharpenRadius != 0; - bool shouldThreshold = baton->threshold != 0; - bool hasOverlay = !baton->overlayPath.empty(); - bool shouldPremultiplyAlpha = HasAlpha(image) && (shouldAffineTransform || shouldBlur || shouldSharpen || hasOverlay); + // Get window size of interpolator, used for determining shrink vs affine + VInterpolate interpolator = VInterpolate::new_from_name(baton->interpolator.data()); + int interpolatorWindowSize = vips_interpolate_get_window_size(interpolator.get_interpolate()); - // Premultiply image alpha channel before all transformations to avoid - // dark fringing around bright pixels - // See: http://entropymine.com/imageworsener/resizealpha/ - if (shouldPremultiplyAlpha) { - VipsImage *imagePremultiplied; - if (vips_premultiply(image, &imagePremultiplied, "max_alpha", maxAlpha, nullptr)) { - (baton->err).append("Failed to premultiply alpha channel."); - return Error(); - } - vips_object_local(hook, imagePremultiplied); - image = imagePremultiplied; - } - - // Use vips_affine with the remaining float part - 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 - double residual = (xresidual + yresidual) / 2.0; - // Apply Gaussian blur before large affine reductions - if (residual < 1.0) { - // Calculate standard deviation - double sigma = ((1.0 / residual) - 0.4) / 3.0; - if (sigma >= 0.3) { - // Sequential input requires a small linecache before use of convolution - if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) { - VipsImage *lineCached; - if (vips_linecache(image, &lineCached, "access", VIPS_ACCESS_SEQUENTIAL, - "tile_height", 1, "threaded", TRUE, nullptr) - ) { - return Error(); + // Scaling calculations + double xfactor = 1.0; + double yfactor = 1.0; + if (baton->width > 0 && baton->height > 0) { + // Fixed width and height + xfactor = static_cast(inputWidth) / static_cast(baton->width); + yfactor = static_cast(inputHeight) / static_cast(baton->height); + switch (baton->canvas) { + case Canvas::CROP: + xfactor = std::min(xfactor, yfactor); + yfactor = xfactor; + break; + case Canvas::EMBED: + xfactor = std::max(xfactor, yfactor); + yfactor = xfactor; + break; + case Canvas::MAX: + if (xfactor > yfactor) { + baton->height = static_cast(round(static_cast(inputHeight) / xfactor)); + yfactor = xfactor; + } else { + baton->width = static_cast(round(static_cast(inputWidth) / yfactor)); + xfactor = yfactor; } - vips_object_local(hook, lineCached); - image = lineCached; - } - // Apply Gaussian blur - VipsImage *blurred; - if (vips_gaussblur(image, &blurred, sigma, nullptr)) { - return Error(); - } - vips_object_local(hook, blurred); - image = blurred; + break; + case Canvas::MIN: + if (xfactor < yfactor) { + baton->height = static_cast(round(static_cast(inputHeight) / xfactor)); + yfactor = xfactor; + } else { + baton->width = static_cast(round(static_cast(inputWidth) / yfactor)); + xfactor = yfactor; + } + break; + case Canvas::IGNORE_ASPECT: + // xfactor, yfactor OK! + break; } - } - // Perform affine transformation - VipsImage *affined; - if (vips_affine(image, &affined, xresidual, 0.0, 0.0, yresidual, - "interpolate", interpolator, nullptr) - ) { - return Error(); - } - vips_object_local(hook, affined); - image = affined; - } - - // Rotate - if (!baton->rotateBeforePreExtract && rotation != Angle::D0) { - VipsImage *rotated; - if (vips_rot(image, &rotated, static_cast(rotation), nullptr)) { - return Error(); - } - vips_object_local(hook, rotated); - image = rotated; - RemoveExifOrientation(image); - } - - // Flip (mirror about Y axis) - if (baton->flip) { - VipsImage *flipped; - if (vips_flip(image, &flipped, VIPS_DIRECTION_VERTICAL, nullptr)) { - return Error(); - } - vips_object_local(hook, flipped); - image = flipped; - RemoveExifOrientation(image); - } - - // Flop (mirror about X axis) - if (baton->flop) { - VipsImage *flopped; - if (vips_flip(image, &flopped, VIPS_DIRECTION_HORIZONTAL, nullptr)) { - return Error(); - } - vips_object_local(hook, flopped); - image = flopped; - RemoveExifOrientation(image); - } - - // Crop/embed - if (image->Xsize != baton->width || image->Ysize != baton->height) { - if (baton->canvas == Canvas::EMBED) { - // Add non-transparent alpha channel, if required - if (baton->background[3] < 255.0 && !HasAlpha(image)) { - // Create single-channel transparency - VipsImage *black; - if (vips_black(&black, image->Xsize, image->Ysize, "bands", 1, nullptr)) { - return Error(); - } - vips_object_local(hook, black); - // Invert to become non-transparent - VipsImage *alpha; - if (vips_invert(black, &alpha, nullptr)) { - return Error(); - } - vips_object_local(hook, alpha); - // Append alpha channel to existing image - VipsImage *joined; - if (vips_bandjoin2(image, alpha, &joined, nullptr)) { - return Error(); - } - vips_object_local(hook, joined); - image = joined; - } - // Create 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)) { - // RGBA - background = vips_array_double_newv(4, - baton->background[0] * multiplier, - baton->background[1] * multiplier, - baton->background[2] * multiplier, - baton->background[3] * multiplier - ); + } else if (baton->width > 0) { + // Fixed width + xfactor = static_cast(inputWidth) / static_cast(baton->width); + if (baton->canvas == Canvas::IGNORE_ASPECT) { + baton->height = inputHeight; } else { - // RGB - background = vips_array_double_newv(3, + // Auto height + yfactor = xfactor; + baton->height = static_cast(round(static_cast(inputHeight) / yfactor)); + } + } else if (baton->height > 0) { + // Fixed height + yfactor = static_cast(inputHeight) / static_cast(baton->height); + if (baton->canvas == Canvas::IGNORE_ASPECT) { + baton->width = inputWidth; + } else { + // Auto width + xfactor = yfactor; + baton->width = static_cast(round(static_cast(inputWidth) / xfactor)); + } + } else { + // Identity transform + baton->width = inputWidth; + baton->height = inputHeight; + } + + // Calculate integral box shrink + int xshrink = CalculateShrink(xfactor, interpolatorWindowSize); + int yshrink = CalculateShrink(yfactor, interpolatorWindowSize); + + // Calculate residual float affine transformation + double xresidual = CalculateResidual(xshrink, xfactor); + double yresidual = CalculateResidual(yshrink, yfactor); + + // Do not enlarge the output if the input width *or* height + // are already less than the required dimensions + if (baton->withoutEnlargement) { + if (inputWidth < baton->width || inputHeight < baton->height) { + xfactor = 1; + yfactor = 1; + xshrink = 1; + yshrink = 1; + xresidual = 0; + yresidual = 0; + baton->width = inputWidth; + baton->height = inputHeight; + } + } + + // If integral x and y shrink are equal, try to use libjpeg shrink-on-load, + // but not when applying gamma correction or pre-resize extract + int shrink_on_load = 1; + if ( + xshrink == yshrink && inputImageType == ImageType::JPEG && xshrink >= 2 && + baton->gamma == 0 && baton->topOffsetPre == -1 + ) { + if (xshrink >= 8) { + xfactor = xfactor / 8; + yfactor = yfactor / 8; + shrink_on_load = 8; + } else if (xshrink >= 4) { + xfactor = xfactor / 4; + yfactor = yfactor / 4; + shrink_on_load = 4; + } else if (xshrink >= 2) { + xfactor = xfactor / 2; + yfactor = yfactor / 2; + shrink_on_load = 2; + } + } + if (shrink_on_load > 1) { + // Recalculate integral shrink and double residual + xfactor = std::max(xfactor, 1.0); + yfactor = std::max(yfactor, 1.0); + xshrink = CalculateShrink(xfactor, interpolatorWindowSize); + yshrink = CalculateShrink(yfactor, interpolatorWindowSize); + xresidual = CalculateResidual(xshrink, xfactor); + yresidual = CalculateResidual(yshrink, yfactor); + // Reload input using shrink-on-load + if (baton->bufferInLength > 1) { + VipsBlob *blob = vips_blob_new(nullptr, baton->bufferIn, baton->bufferInLength); + image = VImage::jpegload_buffer(blob, VImage::option()->set("shrink", shrink_on_load)); + vips_area_unref(reinterpret_cast(blob)); + } else { + image = VImage::jpegload( + const_cast((baton->fileIn).data()), + VImage::option()->set("shrink", shrink_on_load) + ); + } + } + + // Ensure we're using a device-independent colour space + if (HasProfile(image)) { + // Convert to sRGB using embedded profile + try { + image = image.icc_transform(const_cast(srgbProfile.data()), VImage::option() + ->set("embedded", TRUE) + ->set("intent", VIPS_INTENT_PERCEPTUAL) + ); + } catch(...) { + // Ignore failure of embedded profile + } + } else if (image.interpretation() == VIPS_INTERPRETATION_CMYK) { + // Convert to sRGB using default "USWebCoatedSWOP" CMYK profile + std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc"; + image = image.icc_transform(const_cast(srgbProfile.data()), VImage::option() + ->set("input_profile", cmykProfile.data()) + ->set("intent", VIPS_INTENT_PERCEPTUAL) + ); + } + + // Calculate maximum alpha value based on input image pixel depth + double maxAlpha = (image.format() == VIPS_FORMAT_USHORT) ? 65535.0 : 255.0; + + // Flatten image to remove alpha channel + if (baton->flatten && HasAlpha(image)) { + // Scale up 8-bit values to match 16-bit input image + double multiplier = (image.interpretation() == VIPS_INTERPRETATION_RGB16) ? 256.0 : 1.0; + // Background colour + std::vector background { + baton->background[0] * multiplier, + baton->background[1] * multiplier, + baton->background[2] * multiplier + }; + image = image.flatten(VImage::option() + ->set("background", background) + ->set("max_alpha", maxAlpha) + ); + } + + // Negate the colours in the image + if (baton->negate) { + image = image.invert(); + } + + // Gamma encoding (darken) + if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) { + image = image.gamma(VImage::option()->set("exponent", 1.0 / baton->gamma)); + } + + // Convert to greyscale (linear, therefore after gamma encoding, if any) + if (baton->greyscale) { + image = image.colourspace(VIPS_INTERPRETATION_B_W); + } + + if (xshrink > 1 || yshrink > 1) { + image = image.shrink(xshrink, yshrink); + // Recalculate residual float based on dimensions of required vs shrunk images + int shrunkWidth = image.width(); + int shrunkHeight = image.height(); + if (rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270) { + // Swap input output width and height when rotating by 90 or 270 degrees + std::swap(shrunkWidth, shrunkHeight); + } + xresidual = static_cast(baton->width) / static_cast(shrunkWidth); + yresidual = static_cast(baton->height) / static_cast(shrunkHeight); + if (baton->canvas == Canvas::EMBED) { + xresidual = std::min(xresidual, yresidual); + yresidual = xresidual; + } else if (baton->canvas != Canvas::IGNORE_ASPECT) { + xresidual = std::max(xresidual, yresidual); + yresidual = xresidual; + } + } + + bool shouldAffineTransform = xresidual != 0.0 || yresidual != 0.0; + bool shouldBlur = baton->blurSigma != 0.0; + bool shouldSharpen = baton->sharpenRadius != 0; + bool shouldThreshold = baton->threshold != 0; + bool hasOverlay = !baton->overlayPath.empty(); + bool shouldPremultiplyAlpha = HasAlpha(image) && + (shouldAffineTransform || shouldBlur || shouldSharpen || hasOverlay); + + // Premultiply image alpha channel before all transformations to avoid + // dark fringing around bright pixels + // See: http://entropymine.com/imageworsener/resizealpha/ + if (shouldPremultiplyAlpha) { + image = image.premultiply(VImage::option()->set("max_alpha", maxAlpha)); + } + + // Use affine transformation with the remaining float part + if (shouldAffineTransform) { + // Use average of x and y residuals to compute sigma for Gaussian blur + double residual = (xresidual + yresidual) / 2.0; + // Apply Gaussian blur before large affine reductions + if (residual < 1.0) { + // Calculate standard deviation + double sigma = ((1.0 / residual) - 0.4) / 3.0; + if (sigma >= 0.3) { + // Sequential input requires a small linecache before use of convolution + if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) { + image = image.linecache(VImage::option() + ->set("access", VIPS_ACCESS_SEQUENTIAL) + ->set("tile_height", 1) + ->set("threaded", TRUE) + ); + } + // Apply Gaussian blur + image = image.gaussblur(sigma); + } + } + // Perform affine transformation + image = image.affine({xresidual, 0.0, 0.0, yresidual}, VImage::option() + ->set("interpolate", interpolator) + ); + } + + // Rotate + if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) { + image = image.rot(rotation); + RemoveExifOrientation(image); + } + + // Flip (mirror about Y axis) + if (baton->flip) { + image = image.flip(VIPS_DIRECTION_VERTICAL); + RemoveExifOrientation(image); + } + + // Flop (mirror about X axis) + if (baton->flop) { + image = image.flip(VIPS_DIRECTION_HORIZONTAL); + RemoveExifOrientation(image); + } + + // Crop/embed + if (image.width() != baton->width || image.height() != baton->height) { + if (baton->canvas == Canvas::EMBED) { + // Add non-transparent alpha channel, if required + if (baton->background[3] < 255.0 && !HasAlpha(image)) { + image = image.bandjoin(VImage::black(image.width(), image.height()).invert()); + } + // Scale up 8-bit values to match 16-bit input image + double multiplier = (image.interpretation() == VIPS_INTERPRETATION_RGB16) ? 256.0 : 1.0; + // Create background colour + std::vector background { baton->background[0] * multiplier, baton->background[1] * multiplier, baton->background[2] * multiplier + }; + // Add alpha channel to background colour + if (baton->background[3] < 255.0 || HasAlpha(image)) { + background.push_back(baton->background[3] * multiplier); + } + // Embed + int left = static_cast(round((baton->width - image.width()) / 2)); + int top = static_cast(round((baton->height - image.height()) / 2)); + image = image.embed(left, top, baton->width, baton->height, VImage::option() + ->set("extend", VIPS_EXTEND_BACKGROUND) + ->set("background", background) + ); + } else if (baton->canvas != Canvas::IGNORE_ASPECT) { + // Crop/max/min + int left; + int top; + std::tie(left, top) = CalculateCrop( + image.width(), image.height(), baton->width, baton->height, baton->gravity + ); + int width = std::min(image.width(), baton->width); + int height = std::min(image.height(), baton->height); + image = image.extract_area(left, top, width, height); + } + } + + // Post extraction + if (baton->topOffsetPost != -1) { + image = image.extract_area( + baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost + ); + } + + // Threshold - must happen before blurring, due to the utility of blurring after thresholding + if (shouldThreshold) { + image = image.colourspace(VIPS_INTERPRETATION_B_W) >= baton->threshold; + } + + // Blur + if (shouldBlur) { + image = Blur(image, baton->blurSigma); + } + + // Sharpen + if (shouldSharpen) { + image = Sharpen(image, baton->sharpenRadius, baton->sharpenFlat, baton->sharpenJagged); + } + + // Composite with overlay, if present + if (hasOverlay) { + VImage overlayImage; + ImageType overlayImageType = DetermineImageType(baton->overlayPath.data()); + if (overlayImageType != ImageType::UNKNOWN) { + overlayImage = VImage::new_from_file( + baton->overlayPath.data(), + VImage::option()->set("access", baton->accessMethod) + ); + } else { + (baton->err).append("Overlay image is of an unsupported image format"); + return Error(); + } + if (image.format() != VIPS_FORMAT_UCHAR && image.format() != VIPS_FORMAT_FLOAT) { + (baton->err).append("Expected image band format to be uchar or float: "); + (baton->err).append(vips_enum_nick(VIPS_TYPE_BAND_FORMAT, image.format())); + return Error(); + } + if (overlayImage.format() != VIPS_FORMAT_UCHAR && overlayImage.format() != VIPS_FORMAT_FLOAT) { + (baton->err).append("Expected overlay image band format to be uchar or float: "); + (baton->err).append(vips_enum_nick(VIPS_TYPE_BAND_FORMAT, overlayImage.format())); + return Error(); + } + if (!HasAlpha(overlayImage)) { + (baton->err).append("Overlay image must have an alpha channel"); + return Error(); + } + if (overlayImage.width() != image.width() && overlayImage.height() != image.height()) { + (baton->err).append("Overlay image must have same dimensions as resized image"); + return Error(); + } + // Ensure overlay is sRGB and premutiplied + overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB).premultiply(); + + image = Composite(overlayImage, image); + } + + // Reverse premultiplication after all transformations: + if (shouldPremultiplyAlpha) { + image = image.unpremultiply(VImage::option()->set("max_alpha", maxAlpha)); + } + + // Gamma decoding (brighten) + if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) { + image = image.gamma(VImage::option()->set("exponent", baton->gamma)); + } + + // Apply normalization - stretch luminance to cover full dynamic range + if (baton->normalize) { + image = Normalize(image); + } + + // Convert image to sRGB, if not already + if (image.interpretation() == VIPS_INTERPRETATION_RGB16) { + // Cast to integer and fast 8-bit conversion by discarding LSB + image = image.cast(VIPS_FORMAT_USHORT).msb(); + // Explicitly set interpretation to sRGB + image.get_image()->Type = VIPS_INTERPRETATION_sRGB; + } else if (image.interpretation() != VIPS_INTERPRETATION_sRGB) { + // Switch interpretation to sRGB + image = image.colourspace(VIPS_INTERPRETATION_sRGB); + // Transform colours from embedded profile to sRGB profile + if (baton->withMetadata && HasProfile(image)) { + image = image.icc_transform(const_cast(srgbProfile.data()), VImage::option() + ->set("embedded", TRUE) ); } - // Embed - int left = (baton->width - image->Xsize) / 2; - int top = (baton->height - image->Ysize) / 2; - VipsImage *embedded; - if (vips_embed(image, &embedded, left, top, baton->width, baton->height, - "extend", VIPS_EXTEND_BACKGROUND, "background", background, nullptr - )) { - vips_area_unref(reinterpret_cast(background)); - return Error(); - } - vips_area_unref(reinterpret_cast(background)); - vips_object_local(hook, embedded); - image = embedded; - } else if (baton->canvas != Canvas::IGNORE_ASPECT) { - // Crop/max/min - int left; - int top; - std::tie(left, top) = CalculateCrop(image->Xsize, image->Ysize, baton->width, baton->height, baton->gravity); - int width = std::min(image->Xsize, baton->width); - int height = std::min(image->Ysize, baton->height); - VipsImage *extracted; - if (vips_extract_area(image, &extracted, left, top, width, height, nullptr)) { - return Error(); - } - vips_object_local(hook, extracted); - image = extracted; - } - } - - // Post extraction - if (baton->topOffsetPost != -1) { - VipsImage *extractedPost; - if (vips_extract_area(image, &extractedPost, - baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, nullptr - )) { - return Error(); - } - vips_object_local(hook, 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 - if (shouldBlur) { - VipsImage *blurred; - if (Blur(hook, image, &blurred, baton->blurSigma)) { - return Error(); - } - image = blurred; - } - - // Sharpen - if (shouldSharpen) { - VipsImage *sharpened; - if (Sharpen(hook, image, &sharpened, baton->sharpenRadius, baton->sharpenFlat, baton->sharpenJagged)) { - return Error(); - } - image = sharpened; - } - - // Composite with overlay, if present - if (hasOverlay) { - VipsImage *overlayImage = nullptr; - ImageType overlayImageType = ImageType::UNKNOWN; - overlayImageType = DetermineImageType(baton->overlayPath.data()); - if (overlayImageType != ImageType::UNKNOWN) { - overlayImage = InitImage(baton->overlayPath.data(), baton->accessMethod); - if (overlayImage == nullptr) { - (baton->err).append("Overlay image has corrupt header"); - return Error(); - } else { - vips_object_local(hook, overlayImage); - } - } else { - (baton->err).append("Overlay image is of an unsupported image format"); - return Error(); - } - if (image->BandFmt != VIPS_FORMAT_UCHAR && image->BandFmt != VIPS_FORMAT_FLOAT) { - (baton->err).append("Expected image band format to be uchar or float: "); - (baton->err).append(vips_enum_nick(VIPS_TYPE_BAND_FORMAT, image->BandFmt)); - return Error(); - } - if (overlayImage->BandFmt != VIPS_FORMAT_UCHAR && overlayImage->BandFmt != VIPS_FORMAT_FLOAT) { - (baton->err).append("Expected overlay image band format to be uchar or float: "); - (baton->err).append(vips_enum_nick(VIPS_TYPE_BAND_FORMAT, overlayImage->BandFmt)); - return Error(); - } - if (!HasAlpha(overlayImage)) { - (baton->err).append("Overlay image must have an alpha channel"); - return Error(); - } - if (overlayImage->Xsize != image->Xsize && overlayImage->Ysize != image->Ysize) { - (baton->err).append("Overlay image must have same dimensions as resized image"); - return Error(); } - // Ensure overlay is sRGB - VipsImage *overlayImageRGB; - if (vips_colourspace(overlayImage, &overlayImageRGB, VIPS_INTERPRETATION_sRGB, nullptr)) { - return Error(); + // Override EXIF Orientation tag + if (baton->withMetadata && baton->withMetadataOrientation != -1) { + SetExifOrientation(image, baton->withMetadataOrientation); } - vips_object_local(hook, overlayImageRGB); - // Premultiply overlay - VipsImage *overlayImagePremultiplied; - if (vips_premultiply(overlayImageRGB, &overlayImagePremultiplied, nullptr)) { - (baton->err).append("Failed to premultiply alpha channel of overlay image"); - return Error(); - } - vips_object_local(hook, overlayImagePremultiplied); - - VipsImage *composited; - if (Composite(hook, overlayImagePremultiplied, image, &composited)) { - (baton->err).append("Failed to composite images"); - return Error(); - } - vips_object_local(hook, composited); - image = composited; - } - - // Reverse premultiplication after all transformations: - if (shouldPremultiplyAlpha) { - VipsImage *imageUnpremultiplied; - if (vips_unpremultiply(image, &imageUnpremultiplied, "max_alpha", maxAlpha, nullptr)) { - (baton->err).append("Failed to unpremultiply alpha channel"); - return Error(); - } - vips_object_local(hook, imageUnpremultiplied); - image = imageUnpremultiplied; - } - - // Gamma decoding (brighten) - if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) { - VipsImage *gammaDecoded; - if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, nullptr)) { - return Error(); - } - vips_object_local(hook, gammaDecoded); - image = gammaDecoded; - } - - // Apply normalization - stretch luminance to cover full dynamic range - if (baton->normalize) { - VipsImage *normalized; - if (Normalize(hook, image, &normalized)) { - return Error(); - } - image = normalized; - } - - // Convert image to sRGB, if not already - 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 - VipsImage *rgb; - if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, nullptr)) { - return Error(); - } - vips_object_local(hook, rgb); - image = rgb; - // Transform colours from embedded profile to sRGB profile - if (baton->withMetadata && HasProfile(image)) { - VipsImage *profiled; - if (vips_icc_transform(image, &profiled, srgbProfile.data(), "embedded", TRUE, nullptr)) { - return Error(); - } - vips_object_local(hook, profiled); - image = profiled; - } - } - - // Override EXIF Orientation tag - if (baton->withMetadata && baton->withMetadataOrientation != -1) { - SetExifOrientation(image, baton->withMetadataOrientation); - } - - // Output - if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) { - // Write JPEG to buffer - if (vips_jpegsave_buffer( - image, &baton->bufferOut, &baton->bufferOutLength, - "strip", !baton->withMetadata, - "Q", baton->quality, - "optimize_coding", TRUE, - "no_subsample", baton->withoutChromaSubsampling, - "trellis_quant", baton->trellisQuantisation, - "overshoot_deringing", baton->overshootDeringing, - "optimize_scans", baton->optimiseScans, - "interlace", baton->progressive, - nullptr - )) { - return Error(); - } - baton->outputFormat = "jpeg"; - } else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) { - // Write PNG to buffer - if (vips_pngsave_buffer( - 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(); - } - baton->outputFormat = "png"; - } else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == ImageType::WEBP)) { - // Write WEBP to buffer - if (vips_webpsave_buffer( - image, &baton->bufferOut, &baton->bufferOutLength, - "strip", !baton->withMetadata, - "Q", baton->quality, - nullptr - )) { - return Error(); - } - baton->outputFormat = "webp"; - } else if (baton->output == "__raw") { - // Write raw, uncompressed image data to buffer - if (baton->greyscale || image->Type == VIPS_INTERPRETATION_B_W) { - // Extract first band for greyscale image - VipsImage *grey; - if (vips_extract_band(image, &grey, 0, nullptr)) { - return Error(); - } - vips_object_local(hook, grey); - image = grey; - } - if (image->BandFmt != VIPS_FORMAT_UCHAR) { - // Cast pixels to uint8 (unsigned char) - VipsImage *uchar; - if (vips_cast(image, &uchar, VIPS_FORMAT_UCHAR, nullptr)) { - return Error(); - } - vips_object_local(hook, uchar); - image = uchar; - } - // Get raw image data - baton->bufferOut = vips_image_write_to_memory(image, &baton->bufferOutLength); - if (baton->bufferOut == nullptr) { - (baton->err).append("Could not allocate enough memory for raw output"); - return Error(); - } - baton->outputFormat = "raw"; - } else { - bool outputJpeg = IsJpeg(baton->output); - bool outputPng = IsPng(baton->output); - bool outputWebp = IsWebp(baton->output); - bool outputTiff = IsTiff(baton->output); - bool outputDz = IsDz(baton->output); - bool matchInput = !(outputJpeg || outputPng || outputWebp || outputTiff || outputDz); - if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) { - // Write JPEG to file - if (vips_jpegsave( - image, baton->output.data(), - "strip", !baton->withMetadata, - "Q", baton->quality, - "optimize_coding", TRUE, - "no_subsample", baton->withoutChromaSubsampling, - "trellis_quant", baton->trellisQuantisation, - "overshoot_deringing", baton->overshootDeringing, - "optimize_scans", baton->optimiseScans, - "interlace", baton->progressive, - nullptr - )) { - return Error(); - } + // Output + if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) { + // Write JPEG to buffer + baton->bufferOut = static_cast(const_cast(vips_blob_get(image.jpegsave_buffer(VImage::option() + ->set("strip", !baton->withMetadata) + ->set("Q", baton->quality) + ->set("optimize_coding", TRUE) + ->set("no_subsample", baton->withoutChromaSubsampling) + ->set("trellis_quant", baton->trellisQuantisation) + ->set("overshoot_deringing", baton->overshootDeringing) + ->set("optimize_scans", baton->optimiseScans) + ->set("interlace", baton->progressive) + ), &baton->bufferOutLength))); baton->outputFormat = "jpeg"; - } else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) { - // Write PNG to file - if (vips_pngsave( - 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(); - } + } else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) { + // Write PNG to buffer + baton->bufferOut = static_cast(const_cast(vips_blob_get(image.pngsave_buffer(VImage::option() + ->set("strip", !baton->withMetadata) + ->set("compression", baton->compressionLevel) + ->set("interlace", baton->progressive) + ->set("filter", baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL) + ), &baton->bufferOutLength))); baton->outputFormat = "png"; - } else if (outputWebp || (matchInput && inputImageType == ImageType::WEBP)) { - // Write WEBP to file - if (vips_webpsave( - image, baton->output.data(), - "strip", !baton->withMetadata, - "Q", baton->quality, - nullptr - )) { - return Error(); - } + } else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == ImageType::WEBP)) { + // Write WEBP to buffer + baton->bufferOut = static_cast(const_cast(vips_blob_get(image.webpsave_buffer(VImage::option() + ->set("strip", !baton->withMetadata) + ->set("Q", baton->quality) + ), &baton->bufferOutLength))); baton->outputFormat = "webp"; - } else if (outputTiff || (matchInput && inputImageType == ImageType::TIFF)) { - // Write TIFF to file - if (vips_tiffsave( - image, baton->output.data(), - "strip", !baton->withMetadata, - "compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, - "Q", baton->quality, - nullptr - )) { + } else if (baton->output == "__raw") { + // Write raw, uncompressed image data to buffer + if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) { + // Extract first band for greyscale image + image = image[0]; + } + if (image.format() != VIPS_FORMAT_UCHAR) { + // Cast pixels to uint8 (unsigned char) + image = image.cast(VIPS_FORMAT_UCHAR); + } + // Get raw image data + baton->bufferOut = static_cast(image.write_to_memory(&baton->bufferOutLength)); + if (baton->bufferOut == nullptr) { + (baton->err).append("Could not allocate enough memory for raw output"); return Error(); } - baton->outputFormat = "tiff"; - } else if (outputDz) { - // Write DZ to file - if (vips_dzsave( - image, baton->output.data(), - "strip", !baton->withMetadata, - "tile_size", baton->tileSize, - "overlap", baton->tileOverlap, - nullptr - )) { - return Error(); - } - baton->outputFormat = "dz"; + baton->outputFormat = "raw"; } else { - (baton->err).append("Unsupported output " + baton->output); - return Error(); + bool outputJpeg = IsJpeg(baton->output); + bool outputPng = IsPng(baton->output); + bool outputWebp = IsWebp(baton->output); + bool outputTiff = IsTiff(baton->output); + bool outputDz = IsDz(baton->output); + bool matchInput = !(outputJpeg || outputPng || outputWebp || outputTiff || outputDz); + if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) { + // Write JPEG to file + image.jpegsave(const_cast(baton->output.data()), VImage::option() + ->set("strip", !baton->withMetadata) + ->set("Q", baton->quality) + ->set("optimize_coding", TRUE) + ->set("no_subsample", baton->withoutChromaSubsampling) + ->set("trellis_quant", baton->trellisQuantisation) + ->set("overshoot_deringing", baton->overshootDeringing) + ->set("optimize_scans", baton->optimiseScans) + ->set("interlace", baton->progressive) + ); + baton->outputFormat = "jpeg"; + } else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) { + // Write PNG to file + image.pngsave(const_cast(baton->output.data()), VImage::option() + ->set("strip", !baton->withMetadata) + ->set("compression", baton->compressionLevel) + ->set("interlace", baton->progressive) + ->set("filter", baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL) + ); + baton->outputFormat = "png"; + } else if (outputWebp || (matchInput && inputImageType == ImageType::WEBP)) { + // Write WEBP to file + image.webpsave(const_cast(baton->output.data()), VImage::option() + ->set("strip", !baton->withMetadata) + ->set("Q", baton->quality) + ); + baton->outputFormat = "webp"; + } else if (outputTiff || (matchInput && inputImageType == ImageType::TIFF)) { + // Write TIFF to file + image.tiffsave(const_cast(baton->output.data()), VImage::option() + ->set("strip", !baton->withMetadata) + ->set("Q", baton->quality) + ->set("compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) + ); + baton->outputFormat = "tiff"; + } else if (outputDz) { + // Write DZ to file + image.dzsave(const_cast(baton->output.data()), VImage::option() + ->set("strip", !baton->withMetadata) + ->set("tile_size", baton->tileSize) + ->set("overlap", baton->tileOverlap) + ); + baton->outputFormat = "dz"; + } else { + (baton->err).append("Unsupported output " + baton->output); + return Error(); + } } + } catch (VError const &err) { + (baton->err).append(err.what()); } - // Clean up any dangling image references - g_object_unref(hook); // Clean up libvips' per-request data and threads vips_error_clear(); vips_thread_shutdown(); @@ -1082,7 +845,6 @@ class PipelineWorker : public AsyncWorker { private: PipelineBaton *baton; Callback *queueListener; - VipsObject *hook; /* Calculate the angle of rotation and need-to-flip for the output image. @@ -1091,28 +853,28 @@ class PipelineWorker : public AsyncWorker { 2. Use input image EXIF Orientation header - supports mirroring 3. Otherwise default to zero, i.e. no rotation */ - std::tuple - CalculateRotationAndFlip(int const angle, VipsImage const *input) { - Angle rotate = Angle::D0; + std::tuple + CalculateRotationAndFlip(int const angle, VImage image) { + VipsAngle rotate = VIPS_ANGLE_D0; bool flip = FALSE; bool flop = FALSE; if (angle == -1) { - switch(ExifOrientation(input)) { - case 6: rotate = Angle::D90; break; - case 3: rotate = Angle::D180; break; - case 8: rotate = Angle::D270; break; + switch(ExifOrientation(image)) { + case 6: rotate = VIPS_ANGLE_D90; break; + case 3: rotate = VIPS_ANGLE_D180; break; + case 8: rotate = VIPS_ANGLE_D270; break; case 2: flop = TRUE; break; // flop 1 - case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6 - case 4: flop = TRUE; rotate = Angle::D180; break; // flop 3 - case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8 + case 7: flip = TRUE; rotate = VIPS_ANGLE_D90; break; // flip 6 + case 4: flop = TRUE; rotate = VIPS_ANGLE_D180; break; // flop 3 + case 5: flip = TRUE; rotate = VIPS_ANGLE_D270; break; // flip 8 } } else { if (angle == 90) { - rotate = Angle::D90; + rotate = VIPS_ANGLE_D90; } else if (angle == 180) { - rotate = Angle::D180; + rotate = VIPS_ANGLE_D180; } else if (angle == 270) { - rotate = Angle::D270; + rotate = VIPS_ANGLE_D270; } } return std::make_tuple(rotate, flip, flop); @@ -1183,15 +945,9 @@ class PipelineWorker : public AsyncWorker { } /* - Copy then clear the error message. - Unref all transitional images on the hook. Clear all thread-local data. */ void Error() { - // Get libvips' error message - (baton->err).append(vips_error_buffer()); - // Clean up any dangling image references - g_object_unref(hook); // Clean up libvips' per-request data and threads vips_error_clear(); vips_thread_shutdown(); diff --git a/src/sharp.cc b/src/sharp.cc index 75fbd5bd..9cc4c9a8 100644 --- a/src/sharp.cc +++ b/src/sharp.cc @@ -1,5 +1,5 @@ #include -#include +#include #include "nan.h" diff --git a/src/utilities.cc b/src/utilities.cc index e6d1c138..9f121ae5 100644 --- a/src/utilities.cc +++ b/src/utilities.cc @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include "nan.h" @@ -196,103 +196,60 @@ NAN_METHOD(format) { between two images of the same dimensions and number of channels. */ NAN_METHOD(_maxColourDistance) { + using vips::VImage; + using vips::VError; using sharp::DetermineImageType; using sharp::ImageType; - using sharp::InitImage; using sharp::HasAlpha; HandleScope(); - // Create "hook" VipsObject to hang image references from - VipsObject *hook = reinterpret_cast(vips_image_new()); - // Open input files - VipsImage *image1 = nullptr; + VImage image1; ImageType imageType1 = DetermineImageType(*Utf8String(info[0])); if (imageType1 != ImageType::UNKNOWN) { - image1 = InitImage(*Utf8String(info[0]), VIPS_ACCESS_SEQUENTIAL); - if (image1 == nullptr) { - g_object_unref(hook); + try { + image1 = VImage::new_from_file(*Utf8String(info[0])); + } catch (...) { return ThrowError("Input file 1 has corrupt header"); - } else { - vips_object_local(hook, image1); } } else { - g_object_unref(hook); return ThrowError("Input file 1 is of an unsupported image format"); } - VipsImage *image2 = nullptr; + VImage image2; ImageType imageType2 = DetermineImageType(*Utf8String(info[1])); if (imageType2 != ImageType::UNKNOWN) { - image2 = InitImage(*Utf8String(info[1]), VIPS_ACCESS_SEQUENTIAL); - if (image2 == nullptr) { - g_object_unref(hook); + try { + image2 = VImage::new_from_file(*Utf8String(info[1])); + } catch (...) { return ThrowError("Input file 2 has corrupt header"); - } else { - vips_object_local(hook, image2); } } else { - g_object_unref(hook); return ThrowError("Input file 2 is of an unsupported image format"); } - // Ensure same number of channels - if (image1->Bands != image2->Bands) { - g_object_unref(hook); + if (image1.bands() != image2.bands()) { return ThrowError("mismatchedBands"); } // Ensure same dimensions - if (image1->Xsize != image2->Xsize || image1->Ysize != image2->Ysize) { - g_object_unref(hook); + if (image1.width() != image2.width() || image1.height() != image2.height()) { return ThrowError("mismatchedDimensions"); } - // Premultiply and remove alpha - if (HasAlpha(image1)) { - VipsImage *imagePremultiplied1; - if (vips_premultiply(image1, &imagePremultiplied1, nullptr)) { - g_object_unref(hook); - return ThrowError(vips_error_buffer()); - } - vips_object_local(hook, imagePremultiplied1); - VipsImage *imagePremultipliedNoAlpha1; - if (vips_extract_band(image1, &imagePremultipliedNoAlpha1, 1, "n", image1->Bands - 1, nullptr)) { - g_object_unref(hook); - return ThrowError(vips_error_buffer()); - } - vips_object_local(hook, imagePremultipliedNoAlpha1); - image1 = imagePremultipliedNoAlpha1; - } - if (HasAlpha(image2)) { - VipsImage *imagePremultiplied2; - if (vips_premultiply(image2, &imagePremultiplied2, nullptr)) { - g_object_unref(hook); - return ThrowError(vips_error_buffer()); - } - vips_object_local(hook, imagePremultiplied2); - VipsImage *imagePremultipliedNoAlpha2; - if (vips_extract_band(image2, &imagePremultipliedNoAlpha2, 1, "n", image2->Bands - 1, nullptr)) { - g_object_unref(hook); - return ThrowError(vips_error_buffer()); - } - vips_object_local(hook, imagePremultipliedNoAlpha2); - image2 = imagePremultipliedNoAlpha2; - } - - // Calculate colour distance - VipsImage *difference; - if (vips_dE00(image1, image2, &difference, nullptr)) { - g_object_unref(hook); - return ThrowError(vips_error_buffer()); - } - vips_object_local(hook, difference); - // Extract maximum distance double maxColourDistance; - if (vips_max(difference, &maxColourDistance, nullptr)) { - g_object_unref(hook); - return ThrowError(vips_error_buffer()); + try { + // Premultiply and remove alpha + if (HasAlpha(image1)) { + image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1)); + } + if (HasAlpha(image2)) { + image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1)); + } + // Calculate colour distance + maxColourDistance = image1.dE00(image2).max(); + } catch (VError err) { + return ThrowError(err.what()); } - g_object_unref(hook); // Clean up libvips' per-request data and threads vips_error_clear(); diff --git a/test/unit/alpha.js b/test/unit/alpha.js index ecae36ed..51e65aa1 100644 --- a/test/unit/alpha.js +++ b/test/unit/alpha.js @@ -92,7 +92,6 @@ describe('Alpha transparency', function() { var expected = fixtures.expected(BASE_NAME); sharp(fixtures.inputPngAlphaPremultiplicationSmall) .resize(2048, 1536) - .interpolateWith('bicubic') .toFile(actual, function(err) { if (err) { done(err); @@ -109,7 +108,6 @@ describe('Alpha transparency', function() { var expected = fixtures.expected(BASE_NAME); sharp(fixtures.inputPngAlphaPremultiplicationLarge) .resize(1024, 768) - .interpolateWith('bicubic') .toFile(actual, function(err) { if (err) { done(err); diff --git a/test/unit/cpplint.js b/test/unit/cpplint.js index 97164fcf..d02b92ff 100644 --- a/test/unit/cpplint.js +++ b/test/unit/cpplint.js @@ -11,7 +11,9 @@ describe('cpplint', function() { // Ignore cpplint failures, possibly newline-related, on Windows if (process.platform !== 'win32') { // 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); it(file, function(done) { // Lint each source file diff --git a/test/unit/io.js b/test/unit/io.js index 1f50e5f0..79fdf46a 100644 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -640,7 +640,7 @@ describe('Input/output', function() { .toFormat('png') .toBuffer(function(err, data, info) { 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(); } else { assert.strictEqual(true, info.size > 0);