mirror of
https://github.com/lovell/sharp.git
synced 2025-07-11 19:40:14 +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': [{
|
||||
'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',
|
||||
'dependencies': [
|
||||
'libvips-cpp'
|
||||
],
|
||||
# Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
|
||||
'variables': {
|
||||
'variables': {
|
||||
@ -17,7 +64,7 @@
|
||||
'conditions': [
|
||||
['OS != "win"', {
|
||||
# Which version, if any, of libvips is available globally via pkg-config?
|
||||
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips 2>/dev/null || true)'
|
||||
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips-cpp 2>/dev/null || true)'
|
||||
}, {
|
||||
'global_vips_version': ''
|
||||
}]
|
||||
@ -43,18 +90,21 @@
|
||||
'src/sharp.cc',
|
||||
'src/utilities.cc'
|
||||
],
|
||||
'defines': [
|
||||
'_GLIBCXX_USE_CXX11_ABI=0'
|
||||
],
|
||||
'include_dirs': [
|
||||
'<!(node -e "require(\'nan\')")'
|
||||
],
|
||||
'conditions': [
|
||||
['use_global_vips == "true"', {
|
||||
# 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': [
|
||||
['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': [
|
||||
['OS == "win"', {
|
||||
'variables': {
|
||||
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
|
||||
},
|
||||
'libraries': [
|
||||
'<(module_root_dir)/lib/libvips.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()")'
|
||||
},
|
||||
'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
|
||||
]
|
||||
}
|
||||
},
|
||||
}, {
|
||||
|
2
package.json
Normal file → Executable file
2
package.json
Normal file → Executable file
@ -68,7 +68,7 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"config": {
|
||||
"libvips": "8.2.0"
|
||||
"libvips": "8.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -1,14 +1,14 @@
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#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
|
||||
*/
|
||||
|
29
src/common.h
29
src/common.h
@ -2,6 +2,9 @@
|
||||
#define SRC_COMMON_H_
|
||||
|
||||
#include <string>
|
||||
#include <vips/vips8>
|
||||
|
||||
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
|
||||
|
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 <vips/vips.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#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<char*>(g_malloc(exifLength));
|
||||
memcpy(baton->exif, exif, exifLength);
|
||||
}
|
||||
void const *exif = image.get_blob(VIPS_META_EXIF_NAME, &exifLength);
|
||||
baton->exif = static_cast<char*>(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<char*>(g_malloc(iccLength));
|
||||
memcpy(baton->icc, icc, iccLength);
|
||||
}
|
||||
void const *icc = image.get_blob(VIPS_META_ICC_NAME, &iccLength);
|
||||
baton->icc = static_cast<char*>(g_malloc(iccLength));
|
||||
memcpy(baton->icc, icc, iccLength);
|
||||
baton->iccLength = iccLength;
|
||||
}
|
||||
// Drop image reference
|
||||
g_object_unref(image);
|
||||
}
|
||||
// Clean up
|
||||
vips_error_clear();
|
||||
|
@ -1,67 +1,38 @@
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#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
|
||||
|
18
src/operations.h
Executable file → Normal file
18
src/operations.h
Executable file → Normal file
@ -1,34 +1,32 @@
|
||||
#ifndef SRC_OPERATIONS_H_
|
||||
#define SRC_OPERATIONS_H_
|
||||
|
||||
#include <vips/vips8>
|
||||
|
||||
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_
|
||||
|
1338
src/pipeline.cc
1338
src/pipeline.cc
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
#include <node.h>
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#include "nan.h"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <cmath>
|
||||
#include <node.h>
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vips8>
|
||||
#include <vips/vector.h>
|
||||
|
||||
#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<VipsObject*>(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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user