mirror of
https://github.com/lovell/sharp.git
synced 2025-07-12 03:50:13 +02:00
Switch from libvips' C to C++ binding
Requires upgrade to libvips 8.2.1
This commit is contained in:
parent
11329d5e09
commit
5c9c17f1f6
68
binding.gyp
68
binding.gyp
@ -1,6 +1,53 @@
|
|||||||
{
|
{
|
||||||
'targets': [{
|
'targets': [{
|
||||||
|
'target_name': 'libvips-cpp',
|
||||||
|
'conditions': [
|
||||||
|
['OS == "win"', {
|
||||||
|
# Build libvips C++ binding for Windows due to MSVC std library ABI changes
|
||||||
|
'type': 'shared_library',
|
||||||
|
'variables': {
|
||||||
|
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
|
||||||
|
},
|
||||||
|
'defines': [
|
||||||
|
'VIPS_CPLUSPLUS_EXPORTS'
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'src/libvips/cplusplus/VError.cpp',
|
||||||
|
'src/libvips/cplusplus/VInterpolate.cpp',
|
||||||
|
'src/libvips/cplusplus/VImage.cpp'
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'<(module_root_dir)/include',
|
||||||
|
'<(module_root_dir)/include/glib-2.0',
|
||||||
|
'<(module_root_dir)/lib/glib-2.0/include'
|
||||||
|
],
|
||||||
|
'libraries': [
|
||||||
|
'<(module_root_dir)/lib/libvips.lib',
|
||||||
|
'<(module_root_dir)/lib/libglib-2.0.lib',
|
||||||
|
'<(module_root_dir)/lib/libgobject-2.0.lib'
|
||||||
|
],
|
||||||
|
'configurations': {
|
||||||
|
'Release': {
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'ExceptionHandling': 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'msvs_disabled_warnings': [
|
||||||
|
4275
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
# Ignore this target for non-Windows
|
||||||
|
'type': 'none'
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}, {
|
||||||
'target_name': 'sharp',
|
'target_name': 'sharp',
|
||||||
|
'dependencies': [
|
||||||
|
'libvips-cpp'
|
||||||
|
],
|
||||||
# Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
|
# Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
|
||||||
'variables': {
|
'variables': {
|
||||||
'variables': {
|
'variables': {
|
||||||
@ -17,7 +64,7 @@
|
|||||||
'conditions': [
|
'conditions': [
|
||||||
['OS != "win"', {
|
['OS != "win"', {
|
||||||
# Which version, if any, of libvips is available globally via pkg-config?
|
# Which version, if any, of libvips is available globally via pkg-config?
|
||||||
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips 2>/dev/null || true)'
|
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips-cpp 2>/dev/null || true)'
|
||||||
}, {
|
}, {
|
||||||
'global_vips_version': ''
|
'global_vips_version': ''
|
||||||
}]
|
}]
|
||||||
@ -43,18 +90,21 @@
|
|||||||
'src/sharp.cc',
|
'src/sharp.cc',
|
||||||
'src/utilities.cc'
|
'src/utilities.cc'
|
||||||
],
|
],
|
||||||
|
'defines': [
|
||||||
|
'_GLIBCXX_USE_CXX11_ABI=0'
|
||||||
|
],
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
'<!(node -e "require(\'nan\')")'
|
'<!(node -e "require(\'nan\')")'
|
||||||
],
|
],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['use_global_vips == "true"', {
|
['use_global_vips == "true"', {
|
||||||
# Use pkg-config for include and lib
|
# Use pkg-config for include and lib
|
||||||
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips glib-2.0 | sed s\/-I//g)'],
|
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['runtime_link == "static"', {
|
['runtime_link == "static"', {
|
||||||
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips)']
|
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips-cpp)']
|
||||||
}, {
|
}, {
|
||||||
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips)']
|
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)']
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
@ -66,9 +116,6 @@
|
|||||||
],
|
],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['OS == "win"', {
|
['OS == "win"', {
|
||||||
'variables': {
|
|
||||||
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
|
|
||||||
},
|
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'<(module_root_dir)/lib/libvips.lib',
|
'<(module_root_dir)/lib/libvips.lib',
|
||||||
'<(module_root_dir)/lib/libglib-2.0.lib',
|
'<(module_root_dir)/lib/libglib-2.0.lib',
|
||||||
@ -80,6 +127,7 @@
|
|||||||
'download_vips': '<!(LDD_VERSION="<!(ldd --version 2>&1 || true)" node -e "require(\'./binding\').download_vips()")'
|
'download_vips': '<!(LDD_VERSION="<!(ldd --version 2>&1 || true)" node -e "require(\'./binding\').download_vips()")'
|
||||||
},
|
},
|
||||||
'libraries': [
|
'libraries': [
|
||||||
|
'<(module_root_dir)/lib/libvips-cpp.so',
|
||||||
'<(module_root_dir)/lib/libvips.so',
|
'<(module_root_dir)/lib/libvips.so',
|
||||||
'<(module_root_dir)/lib/libglib-2.0.so',
|
'<(module_root_dir)/lib/libglib-2.0.so',
|
||||||
'<(module_root_dir)/lib/libgobject-2.0.so',
|
'<(module_root_dir)/lib/libgobject-2.0.so',
|
||||||
@ -118,6 +166,7 @@
|
|||||||
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
|
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
|
||||||
'CLANG_CXX_LIBRARY': 'libc++',
|
'CLANG_CXX_LIBRARY': 'libc++',
|
||||||
'MACOSX_DEPLOYMENT_TARGET': '10.7',
|
'MACOSX_DEPLOYMENT_TARGET': '10.7',
|
||||||
|
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
|
||||||
'OTHER_CPLUSPLUSFLAGS': [
|
'OTHER_CPLUSPLUSFLAGS': [
|
||||||
'-fexceptions',
|
'-fexceptions',
|
||||||
'-Wall',
|
'-Wall',
|
||||||
@ -130,7 +179,10 @@
|
|||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'ExceptionHandling': 1
|
'ExceptionHandling': 1
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
'msvs_disabled_warnings': [
|
||||||
|
4275
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
2
package.json
Normal file → Executable file
2
package.json
Normal file → Executable file
@ -68,7 +68,7 @@
|
|||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
"config": {
|
||||||
"libvips": "8.2.0"
|
"libvips": "8.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
|
@ -28,7 +28,7 @@ VERSION_WEBP=0.5.0
|
|||||||
VERSION_TIFF=4.0.6
|
VERSION_TIFF=4.0.6
|
||||||
VERSION_MAGICK=6.9.2-10
|
VERSION_MAGICK=6.9.2-10
|
||||||
VERSION_ORC=0.4.24
|
VERSION_ORC=0.4.24
|
||||||
VERSION_VIPS=8.2.0
|
VERSION_VIPS=8.2.1
|
||||||
|
|
||||||
mkdir ${DEPS}/zlib
|
mkdir ${DEPS}/zlib
|
||||||
curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
|
curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
|
||||||
@ -106,11 +106,11 @@ cd ${DEPS}/vips
|
|||||||
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
|
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
|
||||||
&& make install-strip
|
&& make install-strip
|
||||||
|
|
||||||
# Remove the C++ bindings
|
# Remove the old C++ bindings
|
||||||
cd ${TARGET}/include
|
cd ${TARGET}/include
|
||||||
rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h
|
rm -rf vips/vipsc++.h vips/vipscpp.h
|
||||||
cd ${TARGET}/lib
|
cd ${TARGET}/lib
|
||||||
rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.*
|
rm -rf pkgconfig .libs *.la libvipsCC*
|
||||||
|
|
||||||
# Create JSON file of version numbers
|
# Create JSON file of version numbers
|
||||||
cd ${TARGET}
|
cd ${TARGET}
|
||||||
|
@ -13,14 +13,14 @@ fi
|
|||||||
|
|
||||||
docker build -t vips-dev-win win
|
docker build -t vips-dev-win win
|
||||||
WIN_CONTAINER_ID=$(docker run -d vips-dev-win)
|
WIN_CONTAINER_ID=$(docker run -d vips-dev-win)
|
||||||
docker cp $WIN_CONTAINER_ID:/libvips-8.2.0-win.tar.gz .
|
docker cp $WIN_CONTAINER_ID:/libvips-8.2.1-win.tar.gz .
|
||||||
docker rm $WIN_CONTAINER_ID
|
docker rm $WIN_CONTAINER_ID
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
|
|
||||||
docker build -t vips-dev-lin lin
|
docker build -t vips-dev-lin lin
|
||||||
LIN_CONTAINER_ID=$(docker run -d vips-dev-lin)
|
LIN_CONTAINER_ID=$(docker run -d vips-dev-lin)
|
||||||
docker cp $LIN_CONTAINER_ID:/libvips-8.2.0-lin.tar.gz .
|
docker cp $LIN_CONTAINER_ID:/libvips-8.2.1-lin.tar.gz .
|
||||||
docker rm $LIN_CONTAINER_ID
|
docker rm $LIN_CONTAINER_ID
|
||||||
|
|
||||||
# Checksums
|
# Checksums
|
||||||
|
@ -29,7 +29,7 @@ ENV VERSION_ZLIB=1.2.8 \
|
|||||||
VERSION_TIFF=4.0.6 \
|
VERSION_TIFF=4.0.6 \
|
||||||
VERSION_MAGICK=6.9.2-10 \
|
VERSION_MAGICK=6.9.2-10 \
|
||||||
VERSION_ORC=0.4.24 \
|
VERSION_ORC=0.4.24 \
|
||||||
VERSION_VIPS=8.2.0
|
VERSION_VIPS=8.2.1
|
||||||
|
|
||||||
RUN mkdir ${DEPS}/zlib
|
RUN mkdir ${DEPS}/zlib
|
||||||
RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
|
RUN curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
|
||||||
@ -107,11 +107,11 @@ RUN ./configure --prefix=${TARGET} --enable-shared --disable-static --disable-de
|
|||||||
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
|
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
|
||||||
&& make install-strip
|
&& make install-strip
|
||||||
|
|
||||||
# Remove the C++ bindings
|
# Remove the old C++ bindings
|
||||||
WORKDIR ${TARGET}/include
|
WORKDIR ${TARGET}/include
|
||||||
RUN rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h
|
RUN rm -rf vips/vipsc++.h vips/vipscpp.h
|
||||||
WORKDIR ${TARGET}/lib
|
WORKDIR ${TARGET}/lib
|
||||||
RUN rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.*
|
RUN rm -rf pkgconfig .libs *.la libvipsCC*
|
||||||
|
|
||||||
# Create JSON file of version numbers
|
# Create JSON file of version numbers
|
||||||
WORKDIR ${TARGET}
|
WORKDIR ${TARGET}
|
||||||
|
@ -6,12 +6,18 @@ RUN apt-get update && apt-get install -y curl zip
|
|||||||
# Fetch and unzip
|
# Fetch and unzip
|
||||||
RUN mkdir /vips
|
RUN mkdir /vips
|
||||||
WORKDIR /vips
|
WORKDIR /vips
|
||||||
RUN curl -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.zip
|
RUN curl -O http://www.vips.ecs.soton.ac.uk/supported/8.2/win32/vips-dev-w64-8.2.1.zip
|
||||||
RUN unzip vips-dev-w64-8.2.zip
|
RUN unzip vips-dev-w64-8.2.1.zip
|
||||||
|
|
||||||
# Clean and zip
|
# Clean and zip
|
||||||
WORKDIR /vips/vips-dev-8.2
|
WORKDIR /vips/vips-dev-8.2
|
||||||
RUN rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll bin/libstdc++-6.dll
|
RUN rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll bin/libstdc++-6.dll
|
||||||
RUN cp bin/*.dll lib/
|
RUN cp bin/*.dll lib/
|
||||||
RUN cp -r lib64/* lib/
|
RUN cp -r lib64/* lib/
|
||||||
RUN GZIP=-9 tar czf /libvips-8.2.0-win.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll
|
|
||||||
|
# Temporary workaround until libvips 8.2.2 is released
|
||||||
|
RUN curl -L -o include/vips/VError8.h https://raw.githubusercontent.com/jcupitt/libvips/master/cplusplus/include/vips/VError8.h
|
||||||
|
RUN curl -L -o include/vips/VImage8.h https://raw.githubusercontent.com/jcupitt/libvips/master/cplusplus/include/vips/VImage8.h
|
||||||
|
RUN curl -L -o include/vips/vips8 https://raw.githubusercontent.com/jcupitt/libvips/master/cplusplus/include/vips/vips8
|
||||||
|
|
||||||
|
RUN GZIP=-9 tar czf /libvips-8.2.1-win.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <vips/vips.h>
|
#include <vips/vips8>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
// Verify platform and compiler compatibility
|
// Verify platform and compiler compatibility
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 1 && VIPS_PATCH_VERSION < 1))
|
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 2))
|
||||||
#error libvips version 8.1.1+ required - see http://sharp.dimens.io/page/install
|
#error libvips version 8.2.0+ required - see http://sharp.dimens.io/page/install
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
||||||
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
|
#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
|
||||||
|
|
||||||
|
using vips::VImage;
|
||||||
|
|
||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
// How many tasks are in the queue?
|
// How many tasks are in the queue?
|
||||||
@ -58,7 +60,7 @@ namespace sharp {
|
|||||||
ImageType imageType = ImageType::UNKNOWN;
|
ImageType imageType = ImageType::UNKNOWN;
|
||||||
char const *load = vips_foreign_find_load_buffer(buffer, length);
|
char const *load = vips_foreign_find_load_buffer(buffer, length);
|
||||||
if (load != NULL) {
|
if (load != NULL) {
|
||||||
std::string loader = load;
|
std::string const loader = load;
|
||||||
if (EndsWith(loader, "JpegBuffer")) {
|
if (EndsWith(loader, "JpegBuffer")) {
|
||||||
imageType = ImageType::JPEG;
|
imageType = ImageType::JPEG;
|
||||||
} else if (EndsWith(loader, "PngBuffer")) {
|
} else if (EndsWith(loader, "PngBuffer")) {
|
||||||
@ -74,13 +76,6 @@ namespace sharp {
|
|||||||
return imageType;
|
return imageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
|
|
||||||
*/
|
|
||||||
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access) {
|
|
||||||
return vips_image_new_from_buffer(buffer, length, nullptr, "access", access, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Determine image format, reads the first few bytes of the file
|
Determine image format, reads the first few bytes of the file
|
||||||
*/
|
*/
|
||||||
@ -88,7 +83,7 @@ namespace sharp {
|
|||||||
ImageType imageType = ImageType::UNKNOWN;
|
ImageType imageType = ImageType::UNKNOWN;
|
||||||
char const *load = vips_foreign_find_load(file);
|
char const *load = vips_foreign_find_load(file);
|
||||||
if (load != nullptr) {
|
if (load != nullptr) {
|
||||||
std::string loader = load;
|
std::string const loader = load;
|
||||||
if (EndsWith(loader, "JpegFile")) {
|
if (EndsWith(loader, "JpegFile")) {
|
||||||
imageType = ImageType::JPEG;
|
imageType = ImageType::JPEG;
|
||||||
} else if (EndsWith(loader, "Png")) {
|
} else if (EndsWith(loader, "Png")) {
|
||||||
@ -106,77 +101,57 @@ namespace sharp {
|
|||||||
return imageType;
|
return imageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise and return a VipsImage from a file.
|
|
||||||
*/
|
|
||||||
VipsImage* InitImage(char const *file, VipsAccess const access) {
|
|
||||||
return vips_image_new_from_file(file, "access", access, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image have an embedded profile?
|
Does this image have an embedded profile?
|
||||||
*/
|
*/
|
||||||
bool HasProfile(VipsImage *image) {
|
bool HasProfile(VImage image) {
|
||||||
return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE;
|
return (image.get_typeof(VIPS_META_ICC_NAME) != 0) ? TRUE : FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image have an alpha channel?
|
Does this image have an alpha channel?
|
||||||
Uses colour space interpretation with number of channels to guess this.
|
Uses colour space interpretation with number of channels to guess this.
|
||||||
*/
|
*/
|
||||||
bool HasAlpha(VipsImage *image) {
|
bool HasAlpha(VImage image) {
|
||||||
|
int const bands = image.bands();
|
||||||
|
VipsInterpretation const interpretation = image.interpretation();
|
||||||
return (
|
return (
|
||||||
(image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) ||
|
(bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
|
||||||
(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) ||
|
(bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
|
||||||
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK)
|
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get EXIF Orientation of image, if any.
|
Get EXIF Orientation of image, if any.
|
||||||
*/
|
*/
|
||||||
int ExifOrientation(VipsImage const *image) {
|
int ExifOrientation(VImage image) {
|
||||||
int orientation = 0;
|
int orientation = 0;
|
||||||
const char *exif;
|
if (image.get_typeof(EXIF_IFD0_ORIENTATION) != 0) {
|
||||||
if (
|
char const *exif = image.get_string(EXIF_IFD0_ORIENTATION);
|
||||||
vips_image_get_typeof(image, EXIF_IFD0_ORIENTATION) != 0 &&
|
if (exif != nullptr) {
|
||||||
!vips_image_get_string(image, EXIF_IFD0_ORIENTATION, &exif)
|
|
||||||
) {
|
|
||||||
orientation = atoi(&exif[0]);
|
orientation = atoi(&exif[0]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return orientation;
|
return orientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Set EXIF Orientation of image.
|
Set EXIF Orientation of image.
|
||||||
*/
|
*/
|
||||||
void SetExifOrientation(VipsImage *image, int const orientation) {
|
void SetExifOrientation(VImage image, int const orientation) {
|
||||||
char exif[3];
|
char exif[3];
|
||||||
g_snprintf(exif, sizeof(exif), "%d", orientation);
|
g_snprintf(exif, sizeof(exif), "%d", orientation);
|
||||||
vips_image_set_string(image, EXIF_IFD0_ORIENTATION, exif);
|
image.set(EXIF_IFD0_ORIENTATION, exif);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remove EXIF Orientation from image.
|
Remove EXIF Orientation from image.
|
||||||
*/
|
*/
|
||||||
void RemoveExifOrientation(VipsImage *image) {
|
void RemoveExifOrientation(VImage image) {
|
||||||
SetExifOrientation(image, 0);
|
SetExifOrientation(image, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Returns the window size for the named interpolator. For example,
|
|
||||||
a window size of 3 means a 3x3 pixel grid is used for the calculation.
|
|
||||||
*/
|
|
||||||
int InterpolatorWindowSize(char const *name) {
|
|
||||||
VipsInterpolate *interpolator = vips_interpolate_new(name);
|
|
||||||
if (interpolator == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int window_size = vips_interpolate_get_window_size(interpolator);
|
|
||||||
g_object_unref(interpolator);
|
|
||||||
return window_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
|
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
|
||||||
*/
|
*/
|
||||||
|
29
src/common.h
29
src/common.h
@ -2,6 +2,9 @@
|
|||||||
#define SRC_COMMON_H_
|
#define SRC_COMMON_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vips/vips8>
|
||||||
|
|
||||||
|
using vips::VImage;
|
||||||
|
|
||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
@ -38,47 +41,31 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
ImageType DetermineImageType(char const *file);
|
ImageType DetermineImageType(char const *file);
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
|
|
||||||
*/
|
|
||||||
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise and return a VipsImage from a file.
|
|
||||||
*/
|
|
||||||
VipsImage* InitImage(char const *file, VipsAccess const access);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image have an embedded profile?
|
Does this image have an embedded profile?
|
||||||
*/
|
*/
|
||||||
bool HasProfile(VipsImage *image);
|
bool HasProfile(VImage image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image have an alpha channel?
|
Does this image have an alpha channel?
|
||||||
Uses colour space interpretation with number of channels to guess this.
|
Uses colour space interpretation with number of channels to guess this.
|
||||||
*/
|
*/
|
||||||
bool HasAlpha(VipsImage *image);
|
bool HasAlpha(VImage image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get EXIF Orientation of image, if any.
|
Get EXIF Orientation of image, if any.
|
||||||
*/
|
*/
|
||||||
int ExifOrientation(VipsImage const *image);
|
int ExifOrientation(VImage image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Set EXIF Orientation of image.
|
Set EXIF Orientation of image.
|
||||||
*/
|
*/
|
||||||
void SetExifOrientation(VipsImage *image, int const orientation);
|
void SetExifOrientation(VImage image, int const orientation);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remove EXIF Orientation from image.
|
Remove EXIF Orientation from image.
|
||||||
*/
|
*/
|
||||||
void RemoveExifOrientation(VipsImage *image);
|
void RemoveExifOrientation(VImage image);
|
||||||
|
|
||||||
/*
|
|
||||||
Returns the window size for the named interpolator. For example,
|
|
||||||
a window size of 3 means a 3x3 pixel grid is used for the calculation.
|
|
||||||
*/
|
|
||||||
int InterpolatorWindowSize(char const *name);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
|
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
|
||||||
|
52
src/libvips/cplusplus/VError.cpp
Normal file
52
src/libvips/cplusplus/VError.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Code for error type
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (C) 1991-2001 The National Gallery
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /*HAVE_CONFIG_H*/
|
||||||
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <vips/vips8>
|
||||||
|
|
||||||
|
VIPS_NAMESPACE_START
|
||||||
|
|
||||||
|
std::ostream &operator<<( std::ostream &file, const VError &err )
|
||||||
|
{
|
||||||
|
err.ostream_print( file );
|
||||||
|
return( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
void VError::ostream_print( std::ostream &file ) const
|
||||||
|
{
|
||||||
|
file << _what;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIPS_NAMESPACE_END
|
714
src/libvips/cplusplus/VImage.cpp
Normal file
714
src/libvips/cplusplus/VImage.cpp
Normal file
@ -0,0 +1,714 @@
|
|||||||
|
/* Object part of VImage class
|
||||||
|
*
|
||||||
|
* 30/12/14
|
||||||
|
* - allow set enum value from string
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (C) 1991-2001 The National Gallery
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /*HAVE_CONFIG_H*/
|
||||||
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#include <vips/vips8>
|
||||||
|
|
||||||
|
#include <vips/debug.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define VIPS_DEBUG
|
||||||
|
#define VIPS_DEBUG_VERBOSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
VIPS_NAMESPACE_START
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
to_vectorv( int n, ... )
|
||||||
|
{
|
||||||
|
std::vector<double> vector( n );
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start( ap, n );
|
||||||
|
for( int i = 0; i < n; i++ )
|
||||||
|
vector[i] = va_arg( ap, double );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( vector );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
to_vector( double value )
|
||||||
|
{
|
||||||
|
return( to_vectorv( 1, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
to_vector( int n, double array[] )
|
||||||
|
{
|
||||||
|
std::vector<double> vector( n );
|
||||||
|
|
||||||
|
for( int i = 0; i < n; i++ )
|
||||||
|
vector[i] = array[i];
|
||||||
|
|
||||||
|
return( vector );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
negate( std::vector<double> vector )
|
||||||
|
{
|
||||||
|
std::vector<double> new_vector( vector.size() );
|
||||||
|
|
||||||
|
for( unsigned int i = 0; i < vector.size(); i++ )
|
||||||
|
new_vector[i] = vector[i] * -1;
|
||||||
|
|
||||||
|
return( new_vector );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
invert( std::vector<double> vector )
|
||||||
|
{
|
||||||
|
std::vector<double> new_vector( vector.size() );
|
||||||
|
|
||||||
|
for( unsigned int i = 0; i < vector.size(); i++ )
|
||||||
|
new_vector[i] = 1.0 / vector[i];
|
||||||
|
|
||||||
|
return( new_vector );
|
||||||
|
}
|
||||||
|
|
||||||
|
VOption::~VOption()
|
||||||
|
{
|
||||||
|
std::list<Pair *>::iterator i;
|
||||||
|
|
||||||
|
for( i = options.begin(); i != options.end(); ++i )
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// input bool
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, bool value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, G_TYPE_BOOLEAN );
|
||||||
|
g_value_set_boolean( &pair->value, value );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// input int ... this path is used for enums as well
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, int value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, G_TYPE_INT );
|
||||||
|
g_value_set_int( &pair->value, value );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// input double
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, double value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, G_TYPE_DOUBLE );
|
||||||
|
g_value_set_double( &pair->value, value );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, const char *value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, G_TYPE_STRING );
|
||||||
|
g_value_set_string( &pair->value, value );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// input image
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, VImage value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_IMAGE );
|
||||||
|
g_value_set_object( &pair->value, value.get_image() );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// input double array
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, std::vector<double> value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
double *array;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
|
||||||
|
vips_value_set_array_double( &pair->value, NULL,
|
||||||
|
static_cast< int >( value.size() ) );
|
||||||
|
array = vips_value_get_array_double( &pair->value, NULL );
|
||||||
|
|
||||||
|
for( i = 0; i < value.size(); i++ )
|
||||||
|
array[i] = value[i];
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// input image array
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, std::vector<VImage> value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
VipsImage **array;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_ARRAY_IMAGE );
|
||||||
|
vips_value_set_array_image( &pair->value,
|
||||||
|
static_cast< int >( value.size() ) );
|
||||||
|
array = vips_value_get_array_image( &pair->value, NULL );
|
||||||
|
|
||||||
|
for( i = 0; i < value.size(); i++ ) {
|
||||||
|
VipsImage *vips_image = value[i].get_image();
|
||||||
|
|
||||||
|
array[i] = vips_image;
|
||||||
|
g_object_ref( vips_image );
|
||||||
|
}
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// input blob
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, VipsBlob *value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_BLOB );
|
||||||
|
g_value_set_boxed( &pair->value, value );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// output bool
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, bool *value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = false;
|
||||||
|
pair->vbool = value;
|
||||||
|
g_value_init( &pair->value, G_TYPE_BOOLEAN );
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// output int
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, int *value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = false;
|
||||||
|
pair->vint = value;
|
||||||
|
g_value_init( &pair->value, G_TYPE_INT );
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// output double
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, double *value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = false;
|
||||||
|
pair->vdouble = value;
|
||||||
|
g_value_init( &pair->value, G_TYPE_DOUBLE );
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// output image
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, VImage *value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = false;
|
||||||
|
pair->vimage = value;
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_IMAGE );
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// output doublearray
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, std::vector<double> *value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = false;
|
||||||
|
pair->vvector = value;
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// output blob
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, VipsBlob **value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = false;
|
||||||
|
pair->vblob = value;
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_BLOB );
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// just g_object_set_property(), except we allow set enum from string
|
||||||
|
static void
|
||||||
|
set_property( VipsObject *object, const char *name, const GValue *value )
|
||||||
|
{
|
||||||
|
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
|
||||||
|
GType type = G_VALUE_TYPE( value );
|
||||||
|
|
||||||
|
GParamSpec *pspec;
|
||||||
|
VipsArgumentClass *argument_class;
|
||||||
|
VipsArgumentInstance *argument_instance;
|
||||||
|
|
||||||
|
if( vips_object_get_argument( object, name,
|
||||||
|
&pspec, &argument_class, &argument_instance ) ) {
|
||||||
|
vips_warn( NULL, "%s", vips_error_buffer() );
|
||||||
|
vips_error_clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( G_IS_PARAM_SPEC_ENUM( pspec ) &&
|
||||||
|
type == G_TYPE_STRING ) {
|
||||||
|
GType pspec_type = G_PARAM_SPEC_VALUE_TYPE( pspec );
|
||||||
|
|
||||||
|
int enum_value;
|
||||||
|
GValue value2 = { 0 };
|
||||||
|
|
||||||
|
if( (enum_value = vips_enum_from_nick( object_class->nickname,
|
||||||
|
pspec_type, g_value_get_string( value ) )) < 0 ) {
|
||||||
|
vips_warn( NULL, "%s", vips_error_buffer() );
|
||||||
|
vips_error_clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_value_init( &value2, pspec_type );
|
||||||
|
g_value_set_enum( &value2, enum_value );
|
||||||
|
g_object_set_property( G_OBJECT( object ), name, &value2 );
|
||||||
|
g_value_unset( &value2 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_object_set_property( G_OBJECT( object ), name, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk the options and set props on the operation
|
||||||
|
void
|
||||||
|
VOption::set_operation( VipsOperation *operation )
|
||||||
|
{
|
||||||
|
std::list<Pair *>::iterator i;
|
||||||
|
|
||||||
|
for( i = options.begin(); i != options.end(); ++i )
|
||||||
|
if( (*i)->input ) {
|
||||||
|
#ifdef VIPS_DEBUG_VERBOSE
|
||||||
|
printf( "set_operation: " );
|
||||||
|
vips_object_print_name( VIPS_OBJECT( operation ) );
|
||||||
|
char *str_value = g_strdup_value_contents( &(*i)->value );
|
||||||
|
printf( ".%s = %s\n", (*i)->name, str_value );
|
||||||
|
g_free( str_value );
|
||||||
|
#endif /*VIPS_DEBUG_VERBOSE*/
|
||||||
|
|
||||||
|
set_property( VIPS_OBJECT( operation ),
|
||||||
|
(*i)->name, &(*i)->value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk the options and fetch any requested outputs
|
||||||
|
void
|
||||||
|
VOption::get_operation( VipsOperation *operation )
|
||||||
|
{
|
||||||
|
std::list<Pair *>::iterator i;
|
||||||
|
|
||||||
|
for( i = options.begin(); i != options.end(); ++i )
|
||||||
|
if( ! (*i)->input ) {
|
||||||
|
const char *name = (*i)->name;
|
||||||
|
|
||||||
|
g_object_get_property( G_OBJECT( operation ),
|
||||||
|
name, &(*i)->value );
|
||||||
|
|
||||||
|
#ifdef VIPS_DEBUG_VERBOSE
|
||||||
|
printf( "get_operation: " );
|
||||||
|
vips_object_print_name( VIPS_OBJECT( operation ) );
|
||||||
|
char *str_value = g_strdup_value_contents(
|
||||||
|
&(*i)->value );
|
||||||
|
printf( ".%s = %s\n", name, str_value );
|
||||||
|
g_free( str_value );
|
||||||
|
#endif /*VIPS_DEBUG_VERBOSE*/
|
||||||
|
|
||||||
|
GValue *value = &(*i)->value;
|
||||||
|
GType type = G_VALUE_TYPE( value );
|
||||||
|
|
||||||
|
if( type == VIPS_TYPE_IMAGE ) {
|
||||||
|
// rebox object
|
||||||
|
VipsImage *image = VIPS_IMAGE(
|
||||||
|
g_value_get_object( value ) );
|
||||||
|
*((*i)->vimage) = VImage( image );
|
||||||
|
}
|
||||||
|
else if( type == G_TYPE_INT )
|
||||||
|
*((*i)->vint) = g_value_get_int( value );
|
||||||
|
else if( type == G_TYPE_BOOLEAN )
|
||||||
|
*((*i)->vbool) = g_value_get_boolean( value );
|
||||||
|
else if( type == G_TYPE_DOUBLE )
|
||||||
|
*((*i)->vdouble) = g_value_get_double( value );
|
||||||
|
else if( type == VIPS_TYPE_ARRAY_DOUBLE ) {
|
||||||
|
int length;
|
||||||
|
double *array =
|
||||||
|
vips_value_get_array_double( value,
|
||||||
|
&length );
|
||||||
|
int j;
|
||||||
|
|
||||||
|
((*i)->vvector)->resize( length );
|
||||||
|
for( j = 0; j < length; j++ )
|
||||||
|
(*((*i)->vvector))[j] = array[j];
|
||||||
|
}
|
||||||
|
else if( type == VIPS_TYPE_BLOB ) {
|
||||||
|
// our caller gets a reference
|
||||||
|
*((*i)->vblob) =
|
||||||
|
(VipsBlob *) g_value_dup_boxed( value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VImage::call_option_string( const char *operation_name,
|
||||||
|
const char *option_string, VOption *options )
|
||||||
|
{
|
||||||
|
VipsOperation *operation;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_call_by_name: starting for %s ...\n",
|
||||||
|
operation_name );
|
||||||
|
|
||||||
|
if( !(operation = vips_operation_new( operation_name )) ) {
|
||||||
|
if( options )
|
||||||
|
delete options;
|
||||||
|
throw( VError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set str options before vargs options, so the user can't
|
||||||
|
* override things we set deliberately.
|
||||||
|
*/
|
||||||
|
if( option_string &&
|
||||||
|
vips_object_set_from_string( VIPS_OBJECT( operation ),
|
||||||
|
option_string ) ) {
|
||||||
|
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
|
||||||
|
g_object_unref( operation );
|
||||||
|
delete options;
|
||||||
|
throw( VError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( options )
|
||||||
|
options->set_operation( operation );
|
||||||
|
|
||||||
|
/* Build from cache.
|
||||||
|
*/
|
||||||
|
if( vips_cache_operation_buildp( &operation ) ) {
|
||||||
|
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
|
||||||
|
delete options;
|
||||||
|
throw( VError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walk args again, writing output.
|
||||||
|
*/
|
||||||
|
if( options )
|
||||||
|
options->get_operation( operation );
|
||||||
|
|
||||||
|
/* We're done with options!
|
||||||
|
*/
|
||||||
|
delete options;
|
||||||
|
|
||||||
|
/* The operation we have built should now have been reffed by
|
||||||
|
* one of its arguments or have finished its work. Either
|
||||||
|
* way, we can unref.
|
||||||
|
*/
|
||||||
|
g_object_unref( operation );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VImage::call( const char *operation_name, VOption *options )
|
||||||
|
{
|
||||||
|
call_option_string( operation_name, NULL, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_from_file( const char *name, VOption *options )
|
||||||
|
{
|
||||||
|
char filename[VIPS_PATH_MAX];
|
||||||
|
char option_string[VIPS_PATH_MAX];
|
||||||
|
const char *operation_name;
|
||||||
|
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
vips__filename_split8( name, filename, option_string );
|
||||||
|
if( !(operation_name = vips_foreign_find_load( filename )) ) {
|
||||||
|
delete options;
|
||||||
|
throw VError();
|
||||||
|
}
|
||||||
|
|
||||||
|
call_option_string( operation_name, option_string,
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "filename", filename )->
|
||||||
|
set( "out", &out ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_from_buffer( void *buf, size_t len, const char *option_string,
|
||||||
|
VOption *options )
|
||||||
|
{
|
||||||
|
const char *operation_name;
|
||||||
|
VipsBlob *blob;
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
if( !(operation_name = vips_foreign_find_load_buffer( buf, len )) ) {
|
||||||
|
delete options;
|
||||||
|
throw( VError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't take a copy of the data or free it.
|
||||||
|
*/
|
||||||
|
blob = vips_blob_new( NULL, buf, len );
|
||||||
|
options = (options ? options : VImage::option())->
|
||||||
|
set( "buffer", blob )->
|
||||||
|
set( "out", &out );
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
|
call_option_string( operation_name, option_string, options );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_from_image( std::vector<double> pixel )
|
||||||
|
{
|
||||||
|
VImage onepx = VImage::black( 1, 1,
|
||||||
|
VImage::option()->set( "bands", bands() ) );
|
||||||
|
|
||||||
|
onepx = onepx.linear( to_vectorv( 1, 1.0 ), pixel ).cast( format() );
|
||||||
|
|
||||||
|
VImage big = onepx.embed( 0, 0, width(), height(),
|
||||||
|
VImage::option()->set( "extend", VIPS_EXTEND_COPY ) );
|
||||||
|
|
||||||
|
big = big.copy(
|
||||||
|
VImage::option()->
|
||||||
|
set( "interpretation", interpretation() )->
|
||||||
|
set( "xres", xres() )->
|
||||||
|
set( "yres", yres() )->
|
||||||
|
set( "xoffset", xres() )->
|
||||||
|
set( "yoffset", yres() ) );
|
||||||
|
|
||||||
|
return( big );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_from_image( double pixel )
|
||||||
|
{
|
||||||
|
return( new_from_image( to_vectorv( 1, pixel ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_matrix( int width, int height )
|
||||||
|
{
|
||||||
|
return( VImage( vips_image_new_matrix( width, height ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_matrixv( int width, int height, ... )
|
||||||
|
{
|
||||||
|
VImage matrix = new_matrix( width, height );
|
||||||
|
VipsImage *vips_matrix = matrix.get_image();
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start( ap, height );
|
||||||
|
for( int y = 0; y < height; y++ )
|
||||||
|
for( int x = 0; x < width; x++ )
|
||||||
|
*VIPS_MATRIX( vips_matrix, x, y ) =
|
||||||
|
va_arg( ap, double );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( matrix );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VImage::write_to_file( const char *name, VOption *options )
|
||||||
|
{
|
||||||
|
char filename[VIPS_PATH_MAX];
|
||||||
|
char option_string[VIPS_PATH_MAX];
|
||||||
|
const char *operation_name;
|
||||||
|
|
||||||
|
vips__filename_split8( name, filename, option_string );
|
||||||
|
if( !(operation_name = vips_foreign_find_save( filename )) ) {
|
||||||
|
delete options;
|
||||||
|
throw VError();
|
||||||
|
}
|
||||||
|
|
||||||
|
call_option_string( operation_name, option_string,
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "filename", filename ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
|
||||||
|
VOption *options )
|
||||||
|
{
|
||||||
|
char filename[VIPS_PATH_MAX];
|
||||||
|
char option_string[VIPS_PATH_MAX];
|
||||||
|
const char *operation_name;
|
||||||
|
VipsBlob *blob;
|
||||||
|
|
||||||
|
vips__filename_split8( suffix, filename, option_string );
|
||||||
|
if( !(operation_name = vips_foreign_find_save_buffer( filename )) ) {
|
||||||
|
delete options;
|
||||||
|
throw VError();
|
||||||
|
}
|
||||||
|
|
||||||
|
call_option_string( operation_name, option_string,
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "buffer", &blob ) );
|
||||||
|
|
||||||
|
if( blob ) {
|
||||||
|
if( buf ) {
|
||||||
|
*buf = VIPS_AREA( blob )->data;
|
||||||
|
VIPS_AREA( blob )->free_fn = NULL;
|
||||||
|
}
|
||||||
|
if( size )
|
||||||
|
*size = VIPS_AREA( blob )->length;
|
||||||
|
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "vips-operators.cpp"
|
||||||
|
|
||||||
|
std::vector<VImage>
|
||||||
|
VImage::bandsplit( VOption *options )
|
||||||
|
{
|
||||||
|
std::vector<VImage> b;
|
||||||
|
|
||||||
|
for( int i = 0; i < bands(); i++ )
|
||||||
|
b.push_back( extract_band( i ) );
|
||||||
|
|
||||||
|
return( b );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::bandjoin( VImage other, VOption *options )
|
||||||
|
{
|
||||||
|
VImage v[2] = { *this, other };
|
||||||
|
std::vector<VImage> vec( v, v + VIPS_NUMBER( v ) );
|
||||||
|
|
||||||
|
return( bandjoin( vec, options ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::complex<double>
|
||||||
|
VImage::minpos( VOption *options )
|
||||||
|
{
|
||||||
|
double x, y;
|
||||||
|
|
||||||
|
(void) min(
|
||||||
|
(options ? options : VImage::option()) ->
|
||||||
|
set( "x", &x ) ->
|
||||||
|
set( "y", &y ) );
|
||||||
|
|
||||||
|
return( std::complex<double>( x, y ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::complex<double>
|
||||||
|
VImage::maxpos( VOption *options )
|
||||||
|
{
|
||||||
|
double x, y;
|
||||||
|
|
||||||
|
(void) max(
|
||||||
|
(options ? options : VImage::option()) ->
|
||||||
|
set( "x", &x ) ->
|
||||||
|
set( "y", &y ) );
|
||||||
|
|
||||||
|
return( std::complex<double>( x, y ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
VIPS_NAMESPACE_END
|
76
src/libvips/cplusplus/VInterpolate.cpp
Normal file
76
src/libvips/cplusplus/VInterpolate.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* Object part of VInterpolate class
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (C) 1991-2001 The National Gallery
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif /*HAVE_CONFIG_H*/
|
||||||
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#include <vips/vips8>
|
||||||
|
|
||||||
|
#include <vips/debug.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define VIPS_DEBUG
|
||||||
|
#define VIPS_DEBUG_VERBOSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
VIPS_NAMESPACE_START
|
||||||
|
|
||||||
|
VInterpolate
|
||||||
|
VInterpolate::new_from_name( const char *name, VOption *options )
|
||||||
|
{
|
||||||
|
VipsInterpolate *interp;
|
||||||
|
|
||||||
|
if( !(interp = vips_interpolate_new( name )) ) {
|
||||||
|
delete options;
|
||||||
|
throw VError();
|
||||||
|
}
|
||||||
|
delete options;
|
||||||
|
|
||||||
|
VInterpolate out( interp );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, VInterpolate value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_INTERPOLATE );
|
||||||
|
g_value_set_object( &pair->value, value.get_interpolate() );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
VIPS_NAMESPACE_END
|
2738
src/libvips/cplusplus/vips-operators.cpp
Normal file
2738
src/libvips/cplusplus/vips-operators.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
#include <node.h>
|
#include <node.h>
|
||||||
#include <vips/vips.h>
|
#include <vips/vips8>
|
||||||
|
|
||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
@ -29,9 +29,11 @@ using Nan::NewBuffer;
|
|||||||
using Nan::Null;
|
using Nan::Null;
|
||||||
using Nan::Error;
|
using Nan::Error;
|
||||||
|
|
||||||
|
using vips::VImage;
|
||||||
|
using vips::VError;
|
||||||
|
|
||||||
using sharp::ImageType;
|
using sharp::ImageType;
|
||||||
using sharp::DetermineImageType;
|
using sharp::DetermineImageType;
|
||||||
using sharp::InitImage;
|
|
||||||
using sharp::HasProfile;
|
using sharp::HasProfile;
|
||||||
using sharp::HasAlpha;
|
using sharp::HasAlpha;
|
||||||
using sharp::ExifOrientation;
|
using sharp::ExifOrientation;
|
||||||
@ -81,13 +83,14 @@ class MetadataWorker : public AsyncWorker {
|
|||||||
g_atomic_int_dec_and_test(&counterQueue);
|
g_atomic_int_dec_and_test(&counterQueue);
|
||||||
|
|
||||||
ImageType imageType = ImageType::UNKNOWN;
|
ImageType imageType = ImageType::UNKNOWN;
|
||||||
VipsImage *image = nullptr;
|
VImage image;
|
||||||
if (baton->bufferInLength > 0) {
|
if (baton->bufferInLength > 0) {
|
||||||
// From buffer
|
// From buffer
|
||||||
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
||||||
if (imageType != ImageType::UNKNOWN) {
|
if (imageType != ImageType::UNKNOWN) {
|
||||||
image = InitImage(baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
|
try {
|
||||||
if (image == nullptr) {
|
image = VImage::new_from_buffer(baton->bufferIn, baton->bufferInLength, nullptr);
|
||||||
|
} catch (...) {
|
||||||
(baton->err).append("Input buffer has corrupt header");
|
(baton->err).append("Input buffer has corrupt header");
|
||||||
imageType = ImageType::UNKNOWN;
|
imageType = ImageType::UNKNOWN;
|
||||||
}
|
}
|
||||||
@ -98,16 +101,17 @@ class MetadataWorker : public AsyncWorker {
|
|||||||
// From file
|
// From file
|
||||||
imageType = DetermineImageType(baton->fileIn.data());
|
imageType = DetermineImageType(baton->fileIn.data());
|
||||||
if (imageType != ImageType::UNKNOWN) {
|
if (imageType != ImageType::UNKNOWN) {
|
||||||
image = InitImage(baton->fileIn.data(), VIPS_ACCESS_RANDOM);
|
try {
|
||||||
if (image == nullptr) {
|
image = VImage::new_from_file(baton->fileIn.data());
|
||||||
|
} catch (...) {
|
||||||
(baton->err).append("Input file has corrupt header");
|
(baton->err).append("Input file has corrupt header");
|
||||||
imageType = ImageType::UNKNOWN;
|
imageType = ImageType::UNKNOWN;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(baton->err).append("Input file is of an unsupported image format");
|
(baton->err).append("Input file is missing or of an unsupported image format");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (image != nullptr && imageType != ImageType::UNKNOWN) {
|
if (imageType != ImageType::UNKNOWN) {
|
||||||
// Image type
|
// Image type
|
||||||
switch (imageType) {
|
switch (imageType) {
|
||||||
case ImageType::JPEG: baton->format = "jpeg"; break;
|
case ImageType::JPEG: baton->format = "jpeg"; break;
|
||||||
@ -119,37 +123,31 @@ class MetadataWorker : public AsyncWorker {
|
|||||||
case ImageType::UNKNOWN: break;
|
case ImageType::UNKNOWN: break;
|
||||||
}
|
}
|
||||||
// VipsImage attributes
|
// VipsImage attributes
|
||||||
baton->width = image->Xsize;
|
baton->width = image.width();
|
||||||
baton->height = image->Ysize;
|
baton->height = image.height();
|
||||||
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
|
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation());
|
||||||
baton->channels = image->Bands;
|
baton->channels = image.bands();
|
||||||
baton->hasProfile = HasProfile(image);
|
baton->hasProfile = HasProfile(image);
|
||||||
// Derived attributes
|
// Derived attributes
|
||||||
baton->hasAlpha = HasAlpha(image);
|
baton->hasAlpha = HasAlpha(image);
|
||||||
baton->orientation = ExifOrientation(image);
|
baton->orientation = ExifOrientation(image);
|
||||||
// EXIF
|
// EXIF
|
||||||
if (vips_image_get_typeof(image, VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
|
if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
|
||||||
void* exif;
|
|
||||||
size_t exifLength;
|
size_t exifLength;
|
||||||
if (!vips_image_get_blob(image, VIPS_META_EXIF_NAME, &exif, &exifLength)) {
|
void const *exif = image.get_blob(VIPS_META_EXIF_NAME, &exifLength);
|
||||||
baton->exifLength = exifLength;
|
|
||||||
baton->exif = static_cast<char*>(g_malloc(exifLength));
|
baton->exif = static_cast<char*>(g_malloc(exifLength));
|
||||||
memcpy(baton->exif, exif, exifLength);
|
memcpy(baton->exif, exif, exifLength);
|
||||||
}
|
baton->exifLength = exifLength;
|
||||||
}
|
}
|
||||||
// ICC profile
|
// ICC profile
|
||||||
if (vips_image_get_typeof(image, VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
|
if (image.get_typeof(VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
|
||||||
void* icc;
|
|
||||||
size_t iccLength;
|
size_t iccLength;
|
||||||
if (!vips_image_get_blob(image, VIPS_META_ICC_NAME, &icc, &iccLength)) {
|
void const *icc = image.get_blob(VIPS_META_ICC_NAME, &iccLength);
|
||||||
baton->iccLength = iccLength;
|
|
||||||
baton->icc = static_cast<char*>(g_malloc(iccLength));
|
baton->icc = static_cast<char*>(g_malloc(iccLength));
|
||||||
memcpy(baton->icc, icc, iccLength);
|
memcpy(baton->icc, icc, iccLength);
|
||||||
|
baton->iccLength = iccLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Drop image reference
|
|
||||||
g_object_unref(image);
|
|
||||||
}
|
|
||||||
// Clean up
|
// Clean up
|
||||||
vips_error_clear();
|
vips_error_clear();
|
||||||
vips_thread_shutdown();
|
vips_thread_shutdown();
|
||||||
|
@ -1,66 +1,37 @@
|
|||||||
#include <vips/vips.h>
|
#include <vips/vips8>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "operations.h"
|
#include "operations.h"
|
||||||
|
|
||||||
|
using vips::VImage;
|
||||||
|
|
||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Alpha composite src over dst
|
Alpha composite src over dst
|
||||||
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
||||||
*/
|
*/
|
||||||
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out) {
|
VImage Composite(VImage src, VImage dst) {
|
||||||
using sharp::HasAlpha;
|
using sharp::HasAlpha;
|
||||||
|
|
||||||
// Split src into non-alpha and alpha
|
// Split src into non-alpha and alpha
|
||||||
VipsImage *srcWithoutAlpha;
|
VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
|
||||||
if (vips_extract_band(src, &srcWithoutAlpha, 0, "n", src->Bands - 1, nullptr))
|
VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
|
||||||
return -1;
|
|
||||||
vips_object_local(context, srcWithoutAlpha);
|
|
||||||
VipsImage *srcAlpha;
|
|
||||||
if (vips_extract_band(src, &srcAlpha, src->Bands - 1, "n", 1, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, srcAlpha);
|
|
||||||
|
|
||||||
// Split dst into non-alpha and alpha channels
|
// Split dst into non-alpha and alpha channels
|
||||||
VipsImage *dstWithoutAlpha;
|
VImage dstWithoutAlpha;
|
||||||
VipsImage *dstAlpha;
|
VImage dstAlpha;
|
||||||
if (HasAlpha(dst)) {
|
if (HasAlpha(dst)) {
|
||||||
// Non-alpha: extract all-but-last channel
|
// Non-alpha: extract all-but-last channel
|
||||||
if (vips_extract_band(dst, &dstWithoutAlpha, 0, "n", dst->Bands - 1, nullptr)) {
|
dstWithoutAlpha = dst.extract_band(0, VImage::option()->set("n", dst.bands() - 1));
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, dstWithoutAlpha);
|
|
||||||
// Alpha: Extract last channel
|
// Alpha: Extract last channel
|
||||||
if (vips_extract_band(dst, &dstAlpha, dst->Bands - 1, "n", 1, nullptr)) {
|
dstAlpha = dst[dst.bands() - 1] * (1.0 / 255.0);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, dstAlpha);
|
|
||||||
} else {
|
} else {
|
||||||
// Non-alpha: Copy reference
|
// Non-alpha: Copy reference
|
||||||
dstWithoutAlpha = dst;
|
dstWithoutAlpha = dst;
|
||||||
// Alpha: Use blank, opaque (0xFF) image
|
// Alpha: Use blank, opaque (0xFF) image
|
||||||
VipsImage *black;
|
dstAlpha = VImage::black(dst.width(), dst.height()).invert();
|
||||||
if (vips_black(&black, dst->Xsize, dst->Ysize, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
vips_object_local(context, black);
|
|
||||||
if (vips_invert(black, &dstAlpha, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, dstAlpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute normalized input alpha channels:
|
|
||||||
VipsImage *srcAlphaNormalized;
|
|
||||||
if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, srcAlphaNormalized);
|
|
||||||
|
|
||||||
VipsImage *dstAlphaNormalized;
|
|
||||||
if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, dstAlphaNormalized);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Compute normalized output alpha channel:
|
// Compute normalized output alpha channel:
|
||||||
@ -72,22 +43,8 @@ namespace sharp {
|
|||||||
// out_a = src_a + dst_a * (1 - src_a)
|
// out_a = src_a + dst_a * (1 - src_a)
|
||||||
// ^^^^^^^^^^^
|
// ^^^^^^^^^^^
|
||||||
// t0
|
// t0
|
||||||
// ^^^^^^^^^^^^^^^^^^^
|
VImage t0 = srcAlpha.linear(-1.0, 1.0);
|
||||||
// t1
|
VImage outAlphaNormalized = srcAlpha + dstAlpha * t0;
|
||||||
VipsImage *t0;
|
|
||||||
if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, t0);
|
|
||||||
|
|
||||||
VipsImage *t1;
|
|
||||||
if (vips_multiply(dstAlphaNormalized, t0, &t1, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, t1);
|
|
||||||
|
|
||||||
VipsImage *outAlphaNormalized;
|
|
||||||
if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, outAlphaNormalized);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Compute output RGB channels:
|
// Compute output RGB channels:
|
||||||
@ -101,182 +58,86 @@ namespace sharp {
|
|||||||
// premultiplied RGBA image as reversal of premultiplication is handled
|
// premultiplied RGBA image as reversal of premultiplication is handled
|
||||||
// externally.
|
// externally.
|
||||||
//
|
//
|
||||||
VipsImage *t2;
|
VImage outRGBPremultiplied = srcWithoutAlpha + dstWithoutAlpha * t0;
|
||||||
if (vips_multiply(dstWithoutAlpha, t0, &t2, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, t2);
|
|
||||||
|
|
||||||
VipsImage *outRGBPremultiplied;
|
|
||||||
if (vips_add(srcWithoutAlpha, t2, &outRGBPremultiplied, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, outRGBPremultiplied);
|
|
||||||
|
|
||||||
// Denormalize output alpha channel:
|
|
||||||
VipsImage *outAlpha;
|
|
||||||
if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, nullptr))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, outAlpha);
|
|
||||||
|
|
||||||
// Combine RGB and alpha channel into output image:
|
// Combine RGB and alpha channel into output image:
|
||||||
return vips_bandjoin2(outRGBPremultiplied, outAlpha, out, nullptr);
|
return outRGBPremultiplied.bandjoin(outAlphaNormalized * 255.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stretch luminance to cover full dynamic range.
|
* Stretch luminance to cover full dynamic range.
|
||||||
*/
|
*/
|
||||||
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) {
|
VImage Normalize(VImage image) {
|
||||||
// Get original colourspace
|
// Get original colourspace
|
||||||
VipsInterpretation typeBeforeNormalize = image->Type;
|
VipsInterpretation typeBeforeNormalize = image.interpretation();
|
||||||
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
|
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
|
||||||
typeBeforeNormalize = VIPS_INTERPRETATION_sRGB;
|
typeBeforeNormalize = VIPS_INTERPRETATION_sRGB;
|
||||||
}
|
}
|
||||||
// Convert to LAB colourspace
|
// Convert to LAB colourspace
|
||||||
VipsImage *lab;
|
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
|
||||||
if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, lab);
|
|
||||||
// Extract luminance
|
// Extract luminance
|
||||||
VipsImage *luminance;
|
VImage luminance = lab[0];
|
||||||
if (vips_extract_band(lab, &luminance, 0, "n", 1, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, luminance);
|
|
||||||
// Extract chroma
|
|
||||||
VipsImage *chroma;
|
|
||||||
if (vips_extract_band(lab, &chroma, 1, "n", 2, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, chroma);
|
|
||||||
// Find luminance range
|
// Find luminance range
|
||||||
VipsImage *stats;
|
VImage stats = luminance.stats();
|
||||||
if (vips_stats(luminance, &stats, nullptr)) {
|
double min = stats(0, 0)[0];
|
||||||
return -1;
|
double max = stats(1, 0)[0];
|
||||||
}
|
|
||||||
vips_object_local(context, stats);
|
|
||||||
double min = *VIPS_MATRIX(stats, 0, 0);
|
|
||||||
double max = *VIPS_MATRIX(stats, 1, 0);
|
|
||||||
if (min != max) {
|
if (min != max) {
|
||||||
|
// Extract chroma
|
||||||
|
VImage chroma = lab.extract_band(1, VImage::option()->set("n", 2));
|
||||||
|
// Calculate multiplication factor and addition
|
||||||
double f = 100.0 / (max - min);
|
double f = 100.0 / (max - min);
|
||||||
double a = -(min * f);
|
double a = -(min * f);
|
||||||
// Scale luminance
|
// Scale luminance, join to chroma, convert back to original colourspace
|
||||||
VipsImage *luminance100;
|
VImage normalized = luminance.linear(f, a).bandjoin(chroma).colourspace(typeBeforeNormalize);
|
||||||
if (vips_linear1(luminance, &luminance100, f, a, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, luminance100);
|
|
||||||
// Join scaled luminance to chroma
|
|
||||||
VipsImage *normalizedLab;
|
|
||||||
if (vips_bandjoin2(luminance100, chroma, &normalizedLab, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, normalizedLab);
|
|
||||||
// Convert to original colourspace
|
|
||||||
VipsImage *normalized;
|
|
||||||
if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, normalized);
|
|
||||||
// Attach original alpha channel, if any
|
// Attach original alpha channel, if any
|
||||||
if (HasAlpha(image)) {
|
if (HasAlpha(image)) {
|
||||||
// Extract original alpha channel
|
// Extract original alpha channel
|
||||||
VipsImage *alpha;
|
VImage alpha = image[image.bands() - 1];
|
||||||
if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, alpha);
|
|
||||||
// Join alpha channel to normalised image
|
// Join alpha channel to normalised image
|
||||||
VipsImage *normalizedAlpha;
|
return normalized.bandjoin(alpha);
|
||||||
if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, normalizedAlpha);
|
|
||||||
*out = normalizedAlpha;
|
|
||||||
} else {
|
} else {
|
||||||
*out = normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Cannot normalise zero-range image
|
|
||||||
*out = image;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gaussian blur (use sigma <0 for fast blur)
|
* Gaussian blur (use sigma <0 for fast blur)
|
||||||
*/
|
*/
|
||||||
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma) {
|
VImage Blur(VImage image, double const sigma) {
|
||||||
VipsImage *blurred;
|
VImage blurred;
|
||||||
if (sigma < 0.0) {
|
if (sigma < 0.0) {
|
||||||
// Fast, mild blur - averages neighbouring pixels
|
// Fast, mild blur - averages neighbouring pixels
|
||||||
VipsImage *blur = vips_image_new_matrixv(3, 3,
|
VImage blur = VImage::new_matrixv(3, 3,
|
||||||
1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0);
|
1.0, 1.0, 1.0);
|
||||||
vips_image_set_double(blur, "scale", 9);
|
blur.set("scale", 9.0);
|
||||||
vips_object_local(context, blur);
|
return image.conv(blur);
|
||||||
if (vips_conv(image, &blurred, blur, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Slower, accurate Gaussian blur
|
// Slower, accurate Gaussian blur
|
||||||
// Create Gaussian function for standard deviation
|
return image.gaussblur(sigma);
|
||||||
VipsImage *gaussian;
|
|
||||||
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
vips_object_local(context, gaussian);
|
|
||||||
// Apply Gaussian function
|
|
||||||
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vips_object_local(context, blurred);
|
|
||||||
*out = blurred;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
|
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
|
||||||
*/
|
*/
|
||||||
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged) {
|
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged) {
|
||||||
VipsImage *sharpened;
|
|
||||||
if (radius == -1) {
|
if (radius == -1) {
|
||||||
// Fast, mild sharpen
|
// Fast, mild sharpen
|
||||||
VipsImage *sharpen = vips_image_new_matrixv(3, 3,
|
VImage sharpen = VImage::new_matrixv(3, 3,
|
||||||
-1.0, -1.0, -1.0,
|
-1.0, -1.0, -1.0,
|
||||||
-1.0, 32.0, -1.0,
|
-1.0, 32.0, -1.0,
|
||||||
-1.0, -1.0, -1.0);
|
-1.0, -1.0, -1.0);
|
||||||
vips_image_set_double(sharpen, "scale", 24);
|
sharpen.set("scale", 24.0);
|
||||||
vips_object_local(context, sharpen);
|
return image.conv(sharpen);
|
||||||
if (vips_conv(image, &sharpened, sharpen, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
|
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
|
||||||
if (vips_sharpen(image, &sharpened, "radius", radius, "m1", flat, "m2", jagged, nullptr)) {
|
return image.sharpen(
|
||||||
return -1;
|
VImage::option()->set("radius", radius)->set("m1", flat)->set("m2", jagged)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vips_object_local(context, sharpened);
|
|
||||||
*out = sharpened;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Threshold(VipsObject *context, VipsImage *image, VipsImage **out, int threshold) {
|
|
||||||
VipsImage *greyscale;
|
|
||||||
if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, greyscale);
|
|
||||||
image = greyscale;
|
|
||||||
|
|
||||||
VipsImage *thresholded;
|
|
||||||
if (vips_moreeq_const1(image, &thresholded, threshold, nullptr)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vips_object_local(context, thresholded);
|
|
||||||
*out = thresholded;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
18
src/operations.h
Executable file → Normal file
18
src/operations.h
Executable file → Normal file
@ -1,34 +1,32 @@
|
|||||||
#ifndef SRC_OPERATIONS_H_
|
#ifndef SRC_OPERATIONS_H_
|
||||||
#define SRC_OPERATIONS_H_
|
#define SRC_OPERATIONS_H_
|
||||||
|
|
||||||
|
#include <vips/vips8>
|
||||||
|
|
||||||
|
using vips::VImage;
|
||||||
|
|
||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Composite images `src` and `dst` with premultiplied alpha channel and output
|
Composite images `src` and `dst` with premultiplied alpha channel and output
|
||||||
image with premultiplied alpha.
|
image with premultiplied alpha.
|
||||||
*/
|
*/
|
||||||
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out);
|
VImage Composite(VImage src, VImage dst);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stretch luminance to cover full dynamic range.
|
* Stretch luminance to cover full dynamic range.
|
||||||
*/
|
*/
|
||||||
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out);
|
VImage Normalize(VImage image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gaussian blur. Use sigma of -1 for fast blur.
|
* Gaussian blur. Use sigma of -1 for fast blur.
|
||||||
*/
|
*/
|
||||||
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma);
|
VImage Blur(VImage image, double const sigma);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
|
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
|
||||||
*/
|
*/
|
||||||
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged);
|
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged);
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform thresholding on an image. If the image is not greyscale, will convert before thresholding.
|
|
||||||
* Pixels with a greyscale value greater-than-or-equal-to `threshold` will be pure white. All others will be pure black.
|
|
||||||
*/
|
|
||||||
int Threshold(VipsObject *context, VipsImage *image, VipsImage **out, int threshold);
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|
||||||
#endif // SRC_OPERATIONS_H_
|
#endif // SRC_OPERATIONS_H_
|
||||||
|
680
src/pipeline.cc
680
src/pipeline.cc
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
#include <node.h>
|
#include <node.h>
|
||||||
#include <vips/vips.h>
|
#include <vips/vips8>
|
||||||
|
|
||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <node.h>
|
#include <node.h>
|
||||||
#include <vips/vips.h>
|
#include <vips/vips8>
|
||||||
#include <vips/vector.h>
|
#include <vips/vector.h>
|
||||||
|
|
||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
@ -196,103 +196,60 @@ NAN_METHOD(format) {
|
|||||||
between two images of the same dimensions and number of channels.
|
between two images of the same dimensions and number of channels.
|
||||||
*/
|
*/
|
||||||
NAN_METHOD(_maxColourDistance) {
|
NAN_METHOD(_maxColourDistance) {
|
||||||
|
using vips::VImage;
|
||||||
|
using vips::VError;
|
||||||
using sharp::DetermineImageType;
|
using sharp::DetermineImageType;
|
||||||
using sharp::ImageType;
|
using sharp::ImageType;
|
||||||
using sharp::InitImage;
|
|
||||||
using sharp::HasAlpha;
|
using sharp::HasAlpha;
|
||||||
|
|
||||||
HandleScope();
|
HandleScope();
|
||||||
|
|
||||||
// Create "hook" VipsObject to hang image references from
|
|
||||||
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
|
|
||||||
|
|
||||||
// Open input files
|
// Open input files
|
||||||
VipsImage *image1 = nullptr;
|
VImage image1;
|
||||||
ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
|
ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
|
||||||
if (imageType1 != ImageType::UNKNOWN) {
|
if (imageType1 != ImageType::UNKNOWN) {
|
||||||
image1 = InitImage(*Utf8String(info[0]), VIPS_ACCESS_SEQUENTIAL);
|
try {
|
||||||
if (image1 == nullptr) {
|
image1 = VImage::new_from_file(*Utf8String(info[0]));
|
||||||
g_object_unref(hook);
|
} catch (...) {
|
||||||
return ThrowError("Input file 1 has corrupt header");
|
return ThrowError("Input file 1 has corrupt header");
|
||||||
} else {
|
|
||||||
vips_object_local(hook, image1);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError("Input file 1 is of an unsupported image format");
|
return ThrowError("Input file 1 is of an unsupported image format");
|
||||||
}
|
}
|
||||||
VipsImage *image2 = nullptr;
|
VImage image2;
|
||||||
ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
|
ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
|
||||||
if (imageType2 != ImageType::UNKNOWN) {
|
if (imageType2 != ImageType::UNKNOWN) {
|
||||||
image2 = InitImage(*Utf8String(info[1]), VIPS_ACCESS_SEQUENTIAL);
|
try {
|
||||||
if (image2 == nullptr) {
|
image2 = VImage::new_from_file(*Utf8String(info[1]));
|
||||||
g_object_unref(hook);
|
} catch (...) {
|
||||||
return ThrowError("Input file 2 has corrupt header");
|
return ThrowError("Input file 2 has corrupt header");
|
||||||
} else {
|
|
||||||
vips_object_local(hook, image2);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError("Input file 2 is of an unsupported image format");
|
return ThrowError("Input file 2 is of an unsupported image format");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure same number of channels
|
// Ensure same number of channels
|
||||||
if (image1->Bands != image2->Bands) {
|
if (image1.bands() != image2.bands()) {
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError("mismatchedBands");
|
return ThrowError("mismatchedBands");
|
||||||
}
|
}
|
||||||
// Ensure same dimensions
|
// Ensure same dimensions
|
||||||
if (image1->Xsize != image2->Xsize || image1->Ysize != image2->Ysize) {
|
if (image1.width() != image2.width() || image1.height() != image2.height()) {
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError("mismatchedDimensions");
|
return ThrowError("mismatchedDimensions");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double maxColourDistance;
|
||||||
|
try {
|
||||||
// Premultiply and remove alpha
|
// Premultiply and remove alpha
|
||||||
if (HasAlpha(image1)) {
|
if (HasAlpha(image1)) {
|
||||||
VipsImage *imagePremultiplied1;
|
image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
|
||||||
if (vips_premultiply(image1, &imagePremultiplied1, nullptr)) {
|
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError(vips_error_buffer());
|
|
||||||
}
|
|
||||||
vips_object_local(hook, imagePremultiplied1);
|
|
||||||
VipsImage *imagePremultipliedNoAlpha1;
|
|
||||||
if (vips_extract_band(image1, &imagePremultipliedNoAlpha1, 1, "n", image1->Bands - 1, nullptr)) {
|
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError(vips_error_buffer());
|
|
||||||
}
|
|
||||||
vips_object_local(hook, imagePremultipliedNoAlpha1);
|
|
||||||
image1 = imagePremultipliedNoAlpha1;
|
|
||||||
}
|
}
|
||||||
if (HasAlpha(image2)) {
|
if (HasAlpha(image2)) {
|
||||||
VipsImage *imagePremultiplied2;
|
image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1));
|
||||||
if (vips_premultiply(image2, &imagePremultiplied2, nullptr)) {
|
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError(vips_error_buffer());
|
|
||||||
}
|
}
|
||||||
vips_object_local(hook, imagePremultiplied2);
|
|
||||||
VipsImage *imagePremultipliedNoAlpha2;
|
|
||||||
if (vips_extract_band(image2, &imagePremultipliedNoAlpha2, 1, "n", image2->Bands - 1, nullptr)) {
|
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError(vips_error_buffer());
|
|
||||||
}
|
|
||||||
vips_object_local(hook, imagePremultipliedNoAlpha2);
|
|
||||||
image2 = imagePremultipliedNoAlpha2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate colour distance
|
// Calculate colour distance
|
||||||
VipsImage *difference;
|
maxColourDistance = image1.dE00(image2).max();
|
||||||
if (vips_dE00(image1, image2, &difference, nullptr)) {
|
} catch (VError err) {
|
||||||
g_object_unref(hook);
|
return ThrowError(err.what());
|
||||||
return ThrowError(vips_error_buffer());
|
|
||||||
}
|
}
|
||||||
vips_object_local(hook, difference);
|
|
||||||
// Extract maximum distance
|
|
||||||
double maxColourDistance;
|
|
||||||
if (vips_max(difference, &maxColourDistance, nullptr)) {
|
|
||||||
g_object_unref(hook);
|
|
||||||
return ThrowError(vips_error_buffer());
|
|
||||||
}
|
|
||||||
g_object_unref(hook);
|
|
||||||
|
|
||||||
// Clean up libvips' per-request data and threads
|
// Clean up libvips' per-request data and threads
|
||||||
vips_error_clear();
|
vips_error_clear();
|
||||||
|
@ -92,7 +92,6 @@ describe('Alpha transparency', function() {
|
|||||||
var expected = fixtures.expected(BASE_NAME);
|
var expected = fixtures.expected(BASE_NAME);
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
.resize(2048, 1536)
|
.resize(2048, 1536)
|
||||||
.interpolateWith('bicubic')
|
|
||||||
.toFile(actual, function(err) {
|
.toFile(actual, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
done(err);
|
done(err);
|
||||||
@ -109,7 +108,6 @@ describe('Alpha transparency', function() {
|
|||||||
var expected = fixtures.expected(BASE_NAME);
|
var expected = fixtures.expected(BASE_NAME);
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||||
.resize(1024, 768)
|
.resize(1024, 768)
|
||||||
.interpolateWith('bicubic')
|
|
||||||
.toFile(actual, function(err) {
|
.toFile(actual, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
done(err);
|
done(err);
|
||||||
|
@ -11,7 +11,9 @@ describe('cpplint', function() {
|
|||||||
// Ignore cpplint failures, possibly newline-related, on Windows
|
// Ignore cpplint failures, possibly newline-related, on Windows
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
// List C++ source files
|
// List C++ source files
|
||||||
fs.readdirSync(path.join(__dirname, '..', '..', 'src')).forEach(function (source) {
|
fs.readdirSync(path.join(__dirname, '..', '..', 'src')).filter(function(source) {
|
||||||
|
return source !== 'libvips';
|
||||||
|
}).forEach(function(source) {
|
||||||
var file = path.join('src', source);
|
var file = path.join('src', source);
|
||||||
it(file, function(done) {
|
it(file, function(done) {
|
||||||
// Lint each source file
|
// Lint each source file
|
||||||
|
@ -640,7 +640,7 @@ describe('Input/output', function() {
|
|||||||
.toFormat('png')
|
.toFormat('png')
|
||||||
.toBuffer(function(err, data, info) {
|
.toBuffer(function(err, data, info) {
|
||||||
if (err) {
|
if (err) {
|
||||||
assert.strictEqual(0, err.message.indexOf('Input file is of an unsupported image format'));
|
assert.strictEqual(0, err.message.indexOf('Input file is missing or of an unsupported image format'));
|
||||||
done();
|
done();
|
||||||
} else {
|
} else {
|
||||||
assert.strictEqual(true, info.size > 0);
|
assert.strictEqual(true, info.size > 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user