diff options
| author | rimio <vasi.vilvoiu@gmail.com> | 2019-01-10 01:29:40 +0200 |
|---|---|---|
| committer | rimio <vasi.vilvoiu@gmail.com> | 2019-01-10 01:29:40 +0200 |
| commit | de931b95f6dc358b20fdfdf493013fcd673ac70a (patch) | |
| tree | 0b801f83be3281cd77da62892de3b95b61587262 | |
| parent | 41f3b5f445a98500a345b00bc4a3ce111af652e8 (diff) | |
PD120 somewhat working
| -rw-r--r-- | CMakeLists.txt | 12 | ||||
| -rw-r--r-- | src/tools/cimg/CImg.h | 62102 | ||||
| -rw-r--r-- | src/tools/cimg/Licence_CeCILL-C_V1-en.txt | 508 | ||||
| -rw-r--r-- | src/tools/sstv-encode.cpp | 60 | ||||
| -rw-r--r-- | test/test-image-2.bmp | bin | 0 -> 1440722 bytes | |||
| -rw-r--r-- | test/test-image.bmp | bin | 952442 -> 6220922 bytes |
6 files changed, 60 insertions, 62622 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ce0377..720c483 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,22 +43,20 @@ set (ENCODE_TOOL_SOURCES ) # Dependencies -find_library (GLOG_LIBRARY glog) -find_library (LIQUID_LIBRARY liquid) -find_package (gflags REQUIRED) -find_package (X11 REQUIRED) +find_library(GLOG_LIBRARY glog) +find_package(gflags REQUIRED) +find_package(ImageMagick COMPONENTS Magick++) # Library (C compiler) add_library (${PROJECT_NAME} SHARED ${LIB_SOURCES}) set_property (TARGET ${PROJECT_NAME} PROPERTY LINKER_LANGUAGE C) target_include_directories (${PROJECT_NAME} PUBLIC "${SRC_DIR}" PUBLIC "${INCLUDE_DIR}") -target_link_libraries (${PROJECT_NAME} liquid) # Tools (C++ compiler) if (BUILD_TOOLS) add_executable (${PROJECT_NAME}-encode ${ENCODE_TOOL_SOURCES}) set_property (TARGET ${PROJECT_NAME}-encode PROPERTY LINKER_LANGUAGE CXX) set_property (TARGET ${PROJECT_NAME}-encode PROPERTY CXX_STANDARD 14) - target_include_directories(${PROJECT_NAME}-encode PUBLIC "${SRC_DIR}/tools" PUBLIC "${INCLUDE_DIR}") - target_link_libraries (${PROJECT_NAME}-encode ${PROJECT_NAME} glog gflags X11 sndfile) + target_include_directories(${PROJECT_NAME}-encode PUBLIC "${SRC_DIR}/tools" PUBLIC "${INCLUDE_DIR}" PUBLIC "${ImageMagick_INCLUDE_DIRS}") + target_link_libraries (${PROJECT_NAME}-encode ${PROJECT_NAME} glog gflags sndfile ${ImageMagick_LIBRARIES}) endif (BUILD_TOOLS)
\ No newline at end of file diff --git a/src/tools/cimg/CImg.h b/src/tools/cimg/CImg.h deleted file mode 100644 index 958bcdf..0000000 --- a/src/tools/cimg/CImg.h +++ /dev/null @@ -1,62102 +0,0 @@ -/* - # - # File : CImg.h - # ( C++ header file ) - # - # Description : The C++ Template Image Processing Toolkit. - # This file is the main component of the CImg Library project. - # ( http://cimg.eu ) - # - # Project manager : David Tschumperle. - # ( http://tschumperle.users.greyc.fr/ ) - # - # A complete list of contributors is available in file 'README.txt' - # distributed within the CImg package. - # - # Licenses : This file is 'dual-licensed', you have to choose one - # of the two licenses below to apply. - # - # CeCILL-C - # The CeCILL-C license is close to the GNU LGPL. - # ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html ) - # - # or CeCILL v2.1 - # The CeCILL license is compatible with the GNU GPL. - # ( http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html ) - # - # This software is governed either by the CeCILL or the CeCILL-C license - # under French law and abiding by the rules of distribution of free software. - # You can use, modify and or redistribute the software under the terms of - # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA - # at the following URL: "http://www.cecill.info". - # - # As a counterpart to the access to the source code and rights to copy, - # modify and redistribute granted by the license, users are provided only - # with a limited warranty and the software's author, the holder of the - # economic rights, and the successive licensors have only limited - # liability. - # - # In this respect, the user's attention is drawn to the risks associated - # with loading, using, modifying and/or developing or reproducing the - # software by the user in light of its specific status of free software, - # that may mean that it is complicated to manipulate, and that also - # therefore means that it is reserved for developers and experienced - # professionals having in-depth computer knowledge. Users are therefore - # encouraged to load and test the software's suitability as regards their - # requirements in conditions enabling the security of their systems and/or - # data to be ensured and, more generally, to use and operate it in the - # same conditions as regards security. - # - # The fact that you are presently reading this means that you have had - # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms. - # -*/ - -// Set version number of the library. -#ifndef cimg_version -#define cimg_version 244 - -/*----------------------------------------------------------- - # - # Test and possibly auto-set CImg configuration variables - # and include required headers. - # - # If you find that the default configuration variables are - # not adapted to your system, you can override their values - # before including the header file "CImg.h" - # (use the #define directive). - # - ------------------------------------------------------------*/ - -// Include standard C++ headers. -// This is the minimal set of required headers to make CImg-based codes compile. -#include <cstdio> -#include <cstdlib> -#include <cstdarg> -#include <cstring> -#include <cmath> -#include <cfloat> -#include <climits> -#include <ctime> -#include <exception> -#include <algorithm> - -// Detect/configure OS variables. -// -// Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies). -// '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). -// '2' for Microsoft Windows. -// (auto-detection is performed if 'cimg_OS' is not set by the user). -#ifndef cimg_OS -#if defined(unix) || defined(__unix) || defined(__unix__) \ - || defined(linux) || defined(__linux) || defined(__linux__) \ - || defined(sun) || defined(__sun) \ - || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \ - || defined(__FreeBSD__) || defined (__DragonFly__) \ - || defined(sgi) || defined(__sgi) \ - || defined(__MACOSX__) || defined(__APPLE__) \ - || defined(__CYGWIN__) -#define cimg_OS 1 -#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) -#define cimg_OS 2 -#else -#define cimg_OS 0 -#endif -#elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2) -#error CImg Library: Invalid configuration variable 'cimg_OS'. -#error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows'). -#endif -#ifndef cimg_date -#define cimg_date __DATE__ -#endif -#ifndef cimg_time -#define cimg_time __TIME__ -#endif - -// Disable silly warnings on some Microsoft VC++ compilers. -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4127) -#pragma warning(disable:4244) -#pragma warning(disable:4311) -#pragma warning(disable:4312) -#pragma warning(disable:4319) -#pragma warning(disable:4512) -#pragma warning(disable:4571) -#pragma warning(disable:4640) -#pragma warning(disable:4706) -#pragma warning(disable:4710) -#pragma warning(disable:4800) -#pragma warning(disable:4804) -#pragma warning(disable:4820) -#pragma warning(disable:4996) - -#ifndef _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE 1 -#endif -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS 1 -#endif -#ifndef _CRT_NONSTDC_NO_DEPRECATE -#define _CRT_NONSTDC_NO_DEPRECATE 1 -#endif -#endif - -// Define correct string functions for each compiler and OS. -#if cimg_OS==2 && defined(_MSC_VER) -#define cimg_sscanf std::sscanf -#define cimg_sprintf std::sprintf -#define cimg_snprintf cimg::_snprintf -#define cimg_vsnprintf cimg::_vsnprintf -#else -#include <stdio.h> -#if defined(__MACOSX__) || defined(__APPLE__) -#define cimg_sscanf cimg::_sscanf -#define cimg_sprintf cimg::_sprintf -#define cimg_snprintf cimg::_snprintf -#define cimg_vsnprintf cimg::_vsnprintf -#else -#define cimg_sscanf std::sscanf -#define cimg_sprintf std::sprintf -#define cimg_snprintf snprintf -#define cimg_vsnprintf vsnprintf -#endif -#endif - -// Include OS-specific headers. -#if cimg_OS==1 -#include <sys/types.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <unistd.h> -#include <dirent.h> -#include <fnmatch.h> -#elif cimg_OS==2 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include <windows.h> -#ifndef _WIN32_IE -#define _WIN32_IE 0x0400 -#endif -#include <shlobj.h> -#include <process.h> -#include <io.h> -#endif - -// Look for C++11 features. -#ifndef cimg_use_cpp11 -#if __cplusplus>201100 -#define cimg_use_cpp11 1 -#else -#define cimg_use_cpp11 0 -#endif -#endif -#if cimg_use_cpp11==1 -#include <initializer_list> -#include <utility> -#endif - -// Convenient macro to define pragma -#ifdef _MSC_VER -#define cimg_pragma(x) __pragma(x) -#else -#define cimg_pragma(x) _Pragma(#x) -#endif - -// Define own types 'cimg_long/ulong' and 'cimg_int64/uint64' to ensure portability. -// ( constrained to 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' and 'sizeof(cimg_int64/cimg_uint64)=8' ). -#if cimg_OS==2 - -#define cimg_uint64 unsigned __int64 -#define cimg_int64 __int64 -#define cimg_ulong UINT_PTR -#define cimg_long INT_PTR -#ifdef _MSC_VER -#define cimg_fuint64 "%I64u" -#define cimg_fint64 "%I64d" -#else -#define cimg_fuint64 "%llu" -#define cimg_fint64 "%lld" -#endif - -#else - -#if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)) -#define cimg_uint64 unsigned long long -#define cimg_int64 long long -#define cimg_fuint64 "%llu" -#define cimg_fint64 "%lld" -#else -#define cimg_uint64 unsigned long -#define cimg_int64 long -#define cimg_fuint64 "%lu" -#define cimg_fint64 "%ld" -#endif - -#if defined(__arm__) || defined(_M_ARM) -#define cimg_ulong unsigned long long -#define cimg_long long long -#else -#define cimg_ulong unsigned long -#define cimg_long long -#endif - -#endif - -// Configure filename separator. -// -// Filename separator is set by default to '/', except for Windows where it is '\'. -#ifndef cimg_file_separator -#if cimg_OS==2 -#define cimg_file_separator '\\' -#else -#define cimg_file_separator '/' -#endif -#endif - -// Configure verbosity of output messages. -// -// Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode). -// '1' to output library messages on the console. -// '2' to output library messages on a basic dialog window (default behavior). -// '3' to do as '1' + add extra warnings (may slow down the code!). -// '4' to do as '2' + add extra warnings (may slow down the code!). -// -// Define 'cimg_strict_warnings' to replace warning messages by exception throwns. -// -// Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals. -#ifndef cimg_verbosity -#if cimg_OS==2 -#define cimg_verbosity 2 -#else -#define cimg_verbosity 1 -#endif -#elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4) -#error CImg Library: Configuration variable 'cimg_verbosity' is badly defined. -#error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }). -#endif - -// Configure display framework. -// -// Define 'cimg_display' to: '0' to disable display capabilities. -// '1' to use the X-Window framework (X11). -// '2' to use the Microsoft GDI32 framework. -#ifndef cimg_display -#if cimg_OS==0 -#define cimg_display 0 -#elif cimg_OS==1 -#define cimg_display 1 -#elif cimg_OS==2 -#define cimg_display 2 -#endif -#elif !(cimg_display==0 || cimg_display==1 || cimg_display==2) -#error CImg Library: Configuration variable 'cimg_display' is badly defined. -#error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }). -#endif - -// Configure the 'abort' signal handler (does nothing by default). -// A typical signal handler can be defined in your own source like this: -// #define cimg_abort_test if (is_abort) throw CImgAbortException("") -// -// where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method. -// 'cimg_abort_test2' does the same but is called more often (in inner loops). -#if defined(cimg_abort_test) && defined(cimg_use_openmp) - -// Define abort macros to be used with OpenMP. -#ifndef _cimg_abort_init_omp -#define _cimg_abort_init_omp bool _cimg_abort_go_omp = true; cimg::unused(_cimg_abort_go_omp) -#endif -#ifndef _cimg_abort_try_omp -#define _cimg_abort_try_omp if (_cimg_abort_go_omp) try -#endif -#ifndef _cimg_abort_catch_omp -#define _cimg_abort_catch_omp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; } -#endif -#ifdef cimg_abort_test2 -#ifndef _cimg_abort_try_omp2 -#define _cimg_abort_try_omp2 _cimg_abort_try_omp -#endif -#ifndef _cimg_abort_catch_omp2 -#define _cimg_abort_catch_omp2 _cimg_abort_catch_omp -#endif -#ifndef _cimg_abort_catch_fill_omp -#define _cimg_abort_catch_fill_omp \ - catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg<charT>::string(e._message).move_to(is_error); \ - cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; } -#endif -#endif -#endif - -#ifndef _cimg_abort_init_omp -#define _cimg_abort_init_omp -#endif -#ifndef _cimg_abort_try_omp -#define _cimg_abort_try_omp -#endif -#ifndef _cimg_abort_catch_omp -#define _cimg_abort_catch_omp -#endif -#ifndef _cimg_abort_try_omp2 -#define _cimg_abort_try_omp2 -#endif -#ifndef _cimg_abort_catch_omp2 -#define _cimg_abort_catch_omp2 -#endif -#ifndef _cimg_abort_catch_fill_omp -#define _cimg_abort_catch_fill_omp -#endif -#ifndef cimg_abort_init -#define cimg_abort_init -#endif -#ifndef cimg_abort_test -#define cimg_abort_test -#endif -#ifndef cimg_abort_test2 -#define cimg_abort_test2 -#endif - -// Include display-specific headers. -#if cimg_display==1 -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#include <X11/keysym.h> -#include <pthread.h> -#ifdef cimg_use_xshm -#include <sys/ipc.h> -#include <sys/shm.h> -#include <X11/extensions/XShm.h> -#endif -#ifdef cimg_use_xrandr -#include <X11/extensions/Xrandr.h> -#endif -#endif -#ifndef cimg_appname -#define cimg_appname "CImg" -#endif - -// Configure OpenMP support. -// (http://www.openmp.org) -// -// Define 'cimg_use_openmp' to enable OpenMP support (requires OpenMP 3.0+). -// -// OpenMP directives are used in many CImg functions to get -// advantages of multi-core CPUs. -#ifdef cimg_use_openmp -#include <omp.h> -#define cimg_pragma_openmp(p) cimg_pragma(omp p) -#else -#define cimg_pragma_openmp(p) -#endif - -// Configure OpenCV support. -// (http://opencv.willowgarage.com/wiki/) -// -// Define 'cimg_use_opencv' to enable OpenCV support. -// -// OpenCV library may be used to access images from cameras -// (see method 'CImg<T>::load_camera()'). -#ifdef cimg_use_opencv -#ifdef True -#undef True -#define _cimg_redefine_True -#endif -#ifdef False -#undef False -#define _cimg_redefine_False -#endif -#include <cstddef> -#include "cv.h" -#include "highgui.h" -#endif - -// Configure LibPNG support. -// (http://www.libpng.org) -// -// Define 'cimg_use_png' to enable LibPNG support. -// -// PNG library may be used to get a native support of '.png' files. -// (see methods 'CImg<T>::{load,save}_png()'. -#ifdef cimg_use_png -extern "C" { -#include "png.h" -} -#endif - -// Configure LibJPEG support. -// (http://en.wikipedia.org/wiki/Libjpeg) -// -// Define 'cimg_use_jpeg' to enable LibJPEG support. -// -// JPEG library may be used to get a native support of '.jpg' files. -// (see methods 'CImg<T>::{load,save}_jpeg()'). -#ifdef cimg_use_jpeg -extern "C" { -#include "jpeglib.h" -#include "setjmp.h" -} -#endif - -// Configure LibTIFF support. -// (http://www.libtiff.org) -// -// Define 'cimg_use_tiff' to enable LibTIFF support. -// -// TIFF library may be used to get a native support of '.tif' files. -// (see methods 'CImg[List]<T>::{load,save}_tiff()'). -#ifdef cimg_use_tiff -extern "C" { -#define uint64 uint64_hack_ -#define int64 int64_hack_ -#include "tiffio.h" -#undef uint64 -#undef int64 -} -#endif - -// Configure LibMINC2 support. -// (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference) -// -// Define 'cimg_use_minc2' to enable LibMINC2 support. -// -// MINC2 library may be used to get a native support of '.mnc' files. -// (see methods 'CImg<T>::{load,save}_minc2()'). -#ifdef cimg_use_minc2 -#include "minc_io_simple_volume.h" -#include "minc_1_simple.h" -#include "minc_1_simple_rw.h" -#endif - -// Configure Zlib support. -// (http://www.zlib.net) -// -// Define 'cimg_use_zlib' to enable Zlib support. -// -// Zlib library may be used to allow compressed data in '.cimgz' files -// (see methods 'CImg[List]<T>::{load,save}_cimg()'). -#ifdef cimg_use_zlib -extern "C" { -#include "zlib.h" -} -#endif - -// Configure libcurl support. -// (http://curl.haxx.se/libcurl/) -// -// Define 'cimg_use_curl' to enable libcurl support. -// -// Libcurl may be used to get a native support of file downloading from the network. -// (see method 'cimg::load_network()'.) -#ifdef cimg_use_curl -#include "curl/curl.h" -#endif - -// Configure Magick++ support. -// (http://www.imagemagick.org/Magick++) -// -// Define 'cimg_use_magick' to enable Magick++ support. -// -// Magick++ library may be used to get a native support of various image file formats. -// (see methods 'CImg<T>::{load,save}()'). -#ifdef cimg_use_magick -#include "Magick++.h" -#endif - -// Configure FFTW3 support. -// (http://www.fftw.org) -// -// Define 'cimg_use_fftw3' to enable libFFTW3 support. -// -// FFTW3 library may be used to efficiently compute the Fast Fourier Transform -// of image data, without restriction on the image size. -// (see method 'CImg[List]<T>::FFT()'). -#ifdef cimg_use_fftw3 -extern "C" { -#include "fftw3.h" -} -#endif - -// Configure LibBoard support. -// (http://libboard.sourceforge.net/) -// -// Define 'cimg_use_board' to enable Board support. -// -// Board library may be used to draw 3D objects in vector-graphics canvas -// that can be saved as '.ps' or '.svg' files afterwards. -// (see method 'CImg<T>::draw_object3d()'). -#ifdef cimg_use_board -#include "Board.h" -#endif - -// Configure OpenEXR support. -// (http://www.openexr.com/) -// -// Define 'cimg_use_openexr' to enable OpenEXR support. -// -// OpenEXR library may be used to get a native support of '.exr' files. -// (see methods 'CImg<T>::{load,save}_exr()'). -#ifdef cimg_use_openexr -#include "ImfRgbaFile.h" -#include "ImfInputFile.h" -#include "ImfChannelList.h" -#include "ImfMatrixAttribute.h" -#include "ImfArray.h" -#endif - -// Configure TinyEXR support. -// (https://github.com/syoyo/tinyexr) -// -// Define 'cimg_use_tinyexr' to enable TinyEXR support. -// -// TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images. -#ifdef cimg_use_tinyexr -#ifndef TINYEXR_IMPLEMENTATION -#define TINYEXR_IMPLEMENTATION -#endif -#include "tinyexr.h" -#endif - -// Lapack configuration. -// (http://www.netlib.org/lapack) -// -// Define 'cimg_use_lapack' to enable LAPACK support. -// -// Lapack library may be used in several CImg methods to speed up -// matrix computations (eigenvalues, inverse, ...). -#ifdef cimg_use_lapack -extern "C" { - extern void sgetrf_(int*, int*, float*, int*, int*, int*); - extern void sgetri_(int*, float*, int*, int*, float*, int*, int*); - extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*); - extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*); - extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*); - extern void dgetrf_(int*, int*, double*, int*, int*, int*); - extern void dgetri_(int*, double*, int*, int*, double*, int*, int*); - extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*); - extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, - int*, double*, int*, double*, int*, int*); - extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*); - extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*); - extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*); -} -#endif - -// Check if min/max/PI macros are defined. -// -// CImg does not compile if macros 'min', 'max' or 'PI' are defined, -// because it redefines functions min(), max() and const variable PI in the cimg:: namespace. -// so it '#undef' these macros if necessary, and restore them to reasonable -// values at the end of this file. -#ifdef min -#undef min -#define _cimg_redefine_min -#endif -#ifdef max -#undef max -#define _cimg_redefine_max -#endif -#ifdef PI -#undef PI -#define _cimg_redefine_PI -#endif - -// Define 'cimg_library' namespace suffix. -// -// You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work -// with several versions of the library at the same time. -#ifdef cimg_namespace_suffix -#define __cimg_library_suffixed(s) cimg_library_##s -#define _cimg_library_suffixed(s) __cimg_library_suffixed(s) -#define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix) -#else -#define cimg_library_suffixed cimg_library -#endif - -/*------------------------------------------------------------------------------ - # - # Define user-friendly macros. - # - # These CImg macros are prefixed by 'cimg_' and can be used safely in your own - # code. They are useful to parse command line options, or to write image loops. - # - ------------------------------------------------------------------------------*/ - -// Macros to define program usage, and retrieve command line arguments. -#define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false) -#define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0) -#define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage) - -// Macros to define and manipulate local neighborhoods. -#define CImg_2x2(I,T) T I[4]; \ - T& I##cc = I[0]; T& I##nc = I[1]; \ - T& I##cn = I[2]; T& I##nn = I[3]; \ - I##cc = I##nc = \ - I##cn = I##nn = 0 - -#define CImg_3x3(I,T) T I[9]; \ - T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \ - T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \ - T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \ - I##pp = I##cp = I##np = \ - I##pc = I##cc = I##nc = \ - I##pn = I##cn = I##nn = 0 - -#define CImg_4x4(I,T) T I[16]; \ - T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \ - T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \ - T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \ - T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \ - I##pp = I##cp = I##np = I##ap = \ - I##pc = I##cc = I##nc = I##ac = \ - I##pn = I##cn = I##nn = I##an = \ - I##pa = I##ca = I##na = I##aa = 0 - -#define CImg_5x5(I,T) T I[25]; \ - T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \ - T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \ - T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \ - T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \ - T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \ - I##bb = I##pb = I##cb = I##nb = I##ab = \ - I##bp = I##pp = I##cp = I##np = I##ap = \ - I##bc = I##pc = I##cc = I##nc = I##ac = \ - I##bn = I##pn = I##cn = I##nn = I##an = \ - I##ba = I##pa = I##ca = I##na = I##aa = 0 - -#define CImg_2x2x2(I,T) T I[8]; \ - T& I##ccc = I[0]; T& I##ncc = I[1]; \ - T& I##cnc = I[2]; T& I##nnc = I[3]; \ - T& I##ccn = I[4]; T& I##ncn = I[5]; \ - T& I##cnn = I[6]; T& I##nnn = I[7]; \ - I##ccc = I##ncc = \ - I##cnc = I##nnc = \ - I##ccn = I##ncn = \ - I##cnn = I##nnn = 0 - -#define CImg_3x3x3(I,T) T I[27]; \ - T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \ - T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \ - T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \ - T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \ - T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \ - T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \ - T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \ - T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \ - T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \ - I##ppp = I##cpp = I##npp = \ - I##pcp = I##ccp = I##ncp = \ - I##pnp = I##cnp = I##nnp = \ - I##ppc = I##cpc = I##npc = \ - I##pcc = I##ccc = I##ncc = \ - I##pnc = I##cnc = I##nnc = \ - I##ppn = I##cpn = I##npn = \ - I##pcn = I##ccn = I##ncn = \ - I##pnn = I##cnn = I##nnn = 0 - -#define cimg_get2x2(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ - I[3] = (T)(img)(_n1##x,_n1##y,z,c) - -#define cimg_get3x3(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ - I[3] = (T)(img)(_p1##x,y,z,c), I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), \ - I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), I[8] = (T)(img)(_n1##x,_n1##y,z,c) - -#define cimg_get4x4(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ - I[3] = (T)(img)(_n2##x,_p1##y,z,c), I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), \ - I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), I[8] = (T)(img)(_p1##x,_n1##y,z,c), \ - I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \ - I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), \ - I[15] = (T)(img)(_n2##x,_n2##y,z,c) - -#define cimg_get5x5(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ - I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), \ - I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), I[8] = (T)(img)(_n1##x,_p1##y,z,c), \ - I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \ - I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), \ - I[15] = (T)(img)(_p2##x,_n1##y,z,c), I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), \ - I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), I[20] = (T)(img)(_p2##x,_n2##y,z,c), \ - I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \ - I[24] = (T)(img)(_n2##x,_n2##y,z,c) - -#define cimg_get6x6(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ - I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), \ - I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), I[8] = (T)(img)(x,_p1##y,z,c), \ - I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \ - I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), \ - I[15] = (T)(img)(_n1##x,y,z,c), I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), \ - I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), I[20] = (T)(img)(x,_n1##y,z,c), \ - I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \ - I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), \ - I[27] = (T)(img)(_n1##x,_n2##y,z,c), I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), \ - I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), I[32] = (T)(img)(x,_n3##y,z,c), \ - I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c) - -#define cimg_get7x7(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ - I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ - I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), I[8] = (T)(img)(_p2##x,_p2##y,z,c), \ - I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \ - I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), \ - I[15] = (T)(img)(_p2##x,_p1##y,z,c), I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), \ - I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), I[20] = (T)(img)(_n3##x,_p1##y,z,c), \ - I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \ - I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), \ - I[27] = (T)(img)(_n3##x,y,z,c), I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), \ - I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), I[32] = (T)(img)(_n1##x,_n1##y,z,c), \ - I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \ - I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), \ - I[39] = (T)(img)(_n1##x,_n2##y,z,c), I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), \ - I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), I[44] = (T)(img)(_p1##x,_n3##y,z,c), \ - I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \ - I[48] = (T)(img)(_n3##x,_n3##y,z,c) - -#define cimg_get8x8(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ - I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ - I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), I[8] = (T)(img)(_p3##x,_p2##y,z,c), \ - I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \ - I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), \ - I[15] = (T)(img)(_n4##x,_p2##y,z,c), I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), \ - I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), I[20] = (T)(img)(_n1##x,_p1##y,z,c), \ - I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \ - I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), \ - I[27] = (T)(img)(x,y,z,c), I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), \ - I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), I[32] = (T)(img)(_p3##x,_n1##y,z,c), \ - I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \ - I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), \ - I[39] = (T)(img)(_n4##x,_n1##y,z,c), I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), \ - I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), I[44] = (T)(img)(_n1##x,_n2##y,z,c), \ - I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \ - I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), \ - I[51] = (T)(img)(x,_n3##y,z,c), I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), \ - I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), I[56] = (T)(img)(_p3##x,_n4##y,z,c), \ - I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \ - I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), \ - I[63] = (T)(img)(_n4##x,_n4##y,z,c); - -#define cimg_get9x9(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), \ - I[3] = (T)(img)(_p1##x,_p4##y,z,c), I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), \ - I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), I[8] = (T)(img)(_n4##x,_p4##y,z,c), \ - I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \ - I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), \ - I[15] = (T)(img)(_n2##x,_p3##y,z,c), I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), \ - I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), I[20] = (T)(img)(_p2##x,_p2##y,z,c), \ - I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \ - I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), \ - I[27] = (T)(img)(_p4##x,_p1##y,z,c), I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), \ - I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), I[32] = (T)(img)(_n1##x,_p1##y,z,c), \ - I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \ - I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), \ - I[39] = (T)(img)(_p1##x,y,z,c), I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), \ - I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), I[44] = (T)(img)(_n4##x,y,z,c), \ - I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \ - I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), \ - I[51] = (T)(img)(_n2##x,_n1##y,z,c), I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), \ - I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), I[56] = (T)(img)(_p2##x,_n2##y,z,c), \ - I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \ - I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), \ - I[63] = (T)(img)(_p4##x,_n3##y,z,c), I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), \ - I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), I[68] = (T)(img)(_n1##x,_n3##y,z,c), \ - I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \ - I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), \ - I[75] = (T)(img)(_p1##x,_n4##y,z,c), I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), \ - I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), I[80] = (T)(img)(_n4##x,_n4##y,z,c) - -#define cimg_get2x2x2(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ - I[3] = (T)(img)(_n1##x,_n1##y,z,c), I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), \ - I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c) - -#define cimg_get3x3x3(img,x,y,z,c,I,T) \ - I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \ - I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), \ - I[5] = (T)(img)(_n1##x,y,_p1##z,c), I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), \ - I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), \ - I[11] = (T)(img)(_n1##x,_p1##y,z,c), I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), \ - I[14] = (T)(img)(_n1##x,y,z,c), I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), \ - I[17] = (T)(img)(_n1##x,_n1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), \ - I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), \ - I[23] = (T)(img)(_n1##x,y,_n1##z,c), I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), \ - I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c) - -// Macros to perform various image loops. -// -// These macros are simpler to use than loops with C++ iterators. -#define cimg_for(img,ptrs,T_ptrs) \ - for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs) -#define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs) -#define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off) -#define cimg_rofoff(img,off) for (cimg_long off = (cimg_long)((img).size() - 1); off>=0; --off) - -#define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) -#define cimg_forX(img,x) cimg_for1((img)._width,x) -#define cimg_forY(img,y) cimg_for1((img)._height,y) -#define cimg_forZ(img,z) cimg_for1((img)._depth,z) -#define cimg_forC(img,c) cimg_for1((img)._spectrum,c) -#define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) -#define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) -#define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) -#define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x) -#define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y) -#define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z) -#define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) -#define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y) -#define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z) -#define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z) -#define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z) - -#define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i) -#define cimg_rofX(img,x) cimg_rof1((img)._width,x) -#define cimg_rofY(img,y) cimg_rof1((img)._height,y) -#define cimg_rofZ(img,z) cimg_rof1((img)._depth,z) -#define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c) -#define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x) -#define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x) -#define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y) -#define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x) -#define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y) -#define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z) -#define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y) -#define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y) -#define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z) -#define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z) -#define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z) - -#define cimg_for_in1(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i) -#define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x) -#define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y) -#define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z) -#define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c) -#define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x) -#define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x) -#define cimg_for_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) cimg_for_inX(img,x0,x1,x) -#define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y) -#define cimg_for_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y) -#define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z) -#define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y) -#define cimg_for_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y) -#define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) -#define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) -#define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x) -#define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y) -#define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth - 1 - (n),z) -#define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c) -#define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) -#define cimg_for_insideXYZ(img,x,y,z,n) \ - cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) -#define cimg_for_insideXYZC(img,x,y,z,c,n) \ - cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) - -#define cimg_for_out1(boundi,i0,i1,i) \ - for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i) -#define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \ - for (int j = 0; j<(int)(boundj); ++j) \ - for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ - ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i)) -#define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \ - for (int k = 0; k<(int)(boundk); ++k) \ - for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ - for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ - ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i)) -#define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \ - for (int l = 0; l<(int)(boundl); ++l) \ - for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \ - for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ - for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \ - i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i)) -#define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x) -#define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y) -#define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z) -#define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c) -#define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y) -#define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z) -#define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c) -#define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z) -#define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c) -#define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c) -#define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \ - cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z) -#define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \ - cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c) -#define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \ - cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c) -#define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \ - cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c) -#define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) -#define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x) -#define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y) -#define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z) -#define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c) -#define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) -#define cimg_for_borderXYZ(img,x,y,z,n) \ - cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) -#define cimg_for_borderXYZC(img,x,y,z,c,n) \ - cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \ - (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c) - -#define cimg_for_spiralXY(img,x,y) \ - for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \ - --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\ - ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1) - -#define cimg_for_lineXY(x,y,x0,y0,x1,y1) \ - for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \ - _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \ - _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \ - _counter = _dx, \ - _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \ - _counter>=0; \ - --_counter, x+=_steep? \ - (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \ - (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx)) - -#define cimg_for2(bound,i) \ - for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ - _n1##i<(int)(bound) || i==--_n1##i; \ - ++i, ++_n1##i) -#define cimg_for2X(img,x) cimg_for2((img)._width,x) -#define cimg_for2Y(img,y) cimg_for2((img)._height,y) -#define cimg_for2Z(img,z) cimg_for2((img)._depth,z) -#define cimg_for2C(img,c) cimg_for2((img)._spectrum,c) -#define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) -#define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) -#define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x) -#define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) -#define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y) -#define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z) -#define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) -#define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z) -#define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z) -#define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z) - -#define cimg_for_in2(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ - i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ - ++i, ++_n1##i) -#define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x) -#define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y) -#define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z) -#define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c) -#define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x) -#define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x) -#define cimg_for_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2X(img,x0,x1,x) -#define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y) -#define cimg_for_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y) -#define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z) -#define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for3(bound,i) \ - for (int i = 0, _p1##i = 0, \ - _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ - _n1##i<(int)(bound) || i==--_n1##i; \ - _p1##i = i++, ++_n1##i) -#define cimg_for3X(img,x) cimg_for3((img)._width,x) -#define cimg_for3Y(img,y) cimg_for3((img)._height,y) -#define cimg_for3Z(img,z) cimg_for3((img)._depth,z) -#define cimg_for3C(img,c) cimg_for3((img)._spectrum,c) -#define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) -#define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) -#define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x) -#define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) -#define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y) -#define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z) -#define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) -#define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z) -#define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z) -#define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z) - -#define cimg_for_in3(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _p1##i = i - 1<0?0:i - 1, \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ - i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ - _p1##i = i++, ++_n1##i) -#define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x) -#define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y) -#define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z) -#define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c) -#define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x) -#define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x) -#define cimg_for_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3X(img,x0,x1,x) -#define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y) -#define cimg_for_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y) -#define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z) -#define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for4(bound,i) \ - for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ - _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ - _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ - _p1##i = i++, ++_n1##i, ++_n2##i) -#define cimg_for4X(img,x) cimg_for4((img)._width,x) -#define cimg_for4Y(img,y) cimg_for4((img)._height,y) -#define cimg_for4Z(img,z) cimg_for4((img)._depth,z) -#define cimg_for4C(img,c) cimg_for4((img)._spectrum,c) -#define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) -#define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) -#define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x) -#define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) -#define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y) -#define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z) -#define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) -#define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z) -#define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z) -#define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z) - -#define cimg_for_in4(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _p1##i = i - 1<0?0:i - 1, \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ - _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ - i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ - _p1##i = i++, ++_n1##i, ++_n2##i) -#define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x) -#define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y) -#define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z) -#define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c) -#define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x) -#define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x) -#define cimg_for_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4X(img,x0,x1,x) -#define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y) -#define cimg_for_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y) -#define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z) -#define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for5(bound,i) \ - for (int i = 0, _p2##i = 0, _p1##i = 0, \ - _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ - _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ - _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ - _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) -#define cimg_for5X(img,x) cimg_for5((img)._width,x) -#define cimg_for5Y(img,y) cimg_for5((img)._height,y) -#define cimg_for5Z(img,z) cimg_for5((img)._depth,z) -#define cimg_for5C(img,c) cimg_for5((img)._spectrum,c) -#define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) -#define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) -#define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x) -#define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) -#define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y) -#define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z) -#define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) -#define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z) -#define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z) -#define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z) - -#define cimg_for_in5(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _p2##i = i - 2<0?0:i - 2, \ - _p1##i = i - 1<0?0:i - 1, \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ - _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ - i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ - _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) -#define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x) -#define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y) -#define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z) -#define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c) -#define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x) -#define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x) -#define cimg_for_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5X(img,x0,x1,x) -#define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y) -#define cimg_for_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y) -#define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z) -#define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for6(bound,i) \ - for (int i = 0, _p2##i = 0, _p1##i = 0, \ - _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ - _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ - _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ - _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ - _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) -#define cimg_for6X(img,x) cimg_for6((img)._width,x) -#define cimg_for6Y(img,y) cimg_for6((img)._height,y) -#define cimg_for6Z(img,z) cimg_for6((img)._depth,z) -#define cimg_for6C(img,c) cimg_for6((img)._spectrum,c) -#define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x) -#define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x) -#define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x) -#define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) -#define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y) -#define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z) -#define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) -#define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z) -#define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z) -#define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z) - -#define cimg_for_in6(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _p2##i = i - 2<0?0:i - 2, \ - _p1##i = i - 1<0?0:i - 1, \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ - _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ - _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ - i<=(int)(i1) && \ - (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ - _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) -#define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x) -#define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y) -#define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z) -#define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c) -#define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x) -#define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x) -#define cimg_for_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6X(img,x0,x1,x) -#define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y) -#define cimg_for_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y) -#define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z) -#define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for7(bound,i) \ - for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ - _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ - _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ - _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ - _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ - _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) -#define cimg_for7X(img,x) cimg_for7((img)._width,x) -#define cimg_for7Y(img,y) cimg_for7((img)._height,y) -#define cimg_for7Z(img,z) cimg_for7((img)._depth,z) -#define cimg_for7C(img,c) cimg_for7((img)._spectrum,c) -#define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x) -#define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x) -#define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x) -#define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) -#define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y) -#define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z) -#define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) -#define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z) -#define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z) -#define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z) - -#define cimg_for_in7(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _p3##i = i - 3<0?0:i - 3, \ - _p2##i = i - 2<0?0:i - 2, \ - _p1##i = i - 1<0?0:i - 1, \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ - _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ - _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ - i<=(int)(i1) && \ - (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ - _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) -#define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x) -#define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y) -#define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z) -#define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c) -#define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x) -#define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x) -#define cimg_for_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7X(img,x0,x1,x) -#define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y) -#define cimg_for_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y) -#define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z) -#define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for8(bound,i) \ - for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ - _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ - _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ - _n3##i = 3>=(bound)?(int)(bound) - 1:3, \ - _n4##i = 4>=(bound)?(int)(bound) - 1:4; \ - _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ - i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ - _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) -#define cimg_for8X(img,x) cimg_for8((img)._width,x) -#define cimg_for8Y(img,y) cimg_for8((img)._height,y) -#define cimg_for8Z(img,z) cimg_for8((img)._depth,z) -#define cimg_for8C(img,c) cimg_for8((img)._spectrum,c) -#define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x) -#define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x) -#define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x) -#define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y) -#define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y) -#define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z) -#define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y) -#define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z) -#define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z) -#define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z) - -#define cimg_for_in8(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _p3##i = i - 3<0?0:i - 3, \ - _p2##i = i - 2<0?0:i - 2, \ - _p1##i = i - 1<0?0:i - 1, \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ - _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ - _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ - _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ - i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ - i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ - _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) -#define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x) -#define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y) -#define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z) -#define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c) -#define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x) -#define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x) -#define cimg_for_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8X(img,x0,x1,x) -#define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y) -#define cimg_for_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y) -#define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z) -#define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for9(bound,i) \ - for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ - _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \ - _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \ - _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \ - _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \ - _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ - i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ - _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) -#define cimg_for9X(img,x) cimg_for9((img)._width,x) -#define cimg_for9Y(img,y) cimg_for9((img)._height,y) -#define cimg_for9Z(img,z) cimg_for9((img)._depth,z) -#define cimg_for9C(img,c) cimg_for9((img)._spectrum,c) -#define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x) -#define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x) -#define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x) -#define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y) -#define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y) -#define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z) -#define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y) -#define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z) -#define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z) -#define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z) - -#define cimg_for_in9(bound,i0,i1,i) \ - for (int i = (int)(i0)<0?0:(int)(i0), \ - _p4##i = i - 4<0?0:i - 4, \ - _p3##i = i - 3<0?0:i - 3, \ - _p2##i = i - 2<0?0:i - 2, \ - _p1##i = i - 1<0?0:i - 1, \ - _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ - _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ - _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ - _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ - i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ - i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ - _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) -#define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x) -#define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y) -#define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z) -#define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c) -#define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x) -#define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x) -#define cimg_for_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9X(img,x0,x1,x) -#define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y) -#define cimg_for_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y) -#define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z) -#define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y) -#define cimg_for_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z) -#define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) -#define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ - cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) - -#define cimg_for2x2(img,x,y,z,c,I,T) \ - cimg_for2((img)._height,y) for (int x = 0, \ - _n1##x = (int)( \ - (I[0] = (T)(img)(0,y,z,c)), \ - (I[2] = (T)(img)(0,_n1##y,z,c)), \ - 1>=(img)._width?(img).width() - 1:1); \ - (_n1##x<(img).width() && ( \ - (I[1] = (T)(img)(_n1##x,y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ - x==--_n1##x; \ - I[0] = I[1], \ - I[2] = I[3], \ - ++x, ++_n1##x) - -#define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _n1##x = (int)( \ - (I[0] = (T)(img)(x,y,z,c)), \ - (I[2] = (T)(img)(x,_n1##y,z,c)), \ - x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ - x<=(int)(x1) && ((_n1##x<(img).width() && ( \ - (I[1] = (T)(img)(_n1##x,y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ - x==--_n1##x); \ - I[0] = I[1], \ - I[2] = I[3], \ - ++x, ++_n1##x) - -#define cimg_for3x3(img,x,y,z,c,I,T) \ - cimg_for3((img)._height,y) for (int x = 0, \ - _p1##x = 0, \ - _n1##x = (int)( \ - (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[3] = I[4] = (T)(img)(0,y,z,c)), \ - (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ - 1>=(img)._width?(img).width() - 1:1); \ - (_n1##x<(img).width() && ( \ - (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,y,z,c)), \ - (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ - x==--_n1##x; \ - I[0] = I[1], I[1] = I[2], \ - I[3] = I[4], I[4] = I[5], \ - I[6] = I[7], I[7] = I[8], \ - _p1##x = x++, ++_n1##x) - -#define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = (int)( \ - (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[3] = (T)(img)(_p1##x,y,z,c)), \ - (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[1] = (T)(img)(x,_p1##y,z,c)), \ - (I[4] = (T)(img)(x,y,z,c)), \ - (I[7] = (T)(img)(x,_n1##y,z,c)), \ - x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ - x<=(int)(x1) && ((_n1##x<(img).width() && ( \ - (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,y,z,c)), \ - (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ - x==--_n1##x); \ - I[0] = I[1], I[1] = I[2], \ - I[3] = I[4], I[4] = I[5], \ - I[6] = I[7], I[7] = I[8], \ - _p1##x = x++, ++_n1##x) - -#define cimg_for4x4(img,x,y,z,c,I,T) \ - cimg_for4((img)._height,y) for (int x = 0, \ - _p1##x = 0, \ - _n1##x = 1>=(img)._width?(img).width() - 1:1, \ - _n2##x = (int)( \ - (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[4] = I[5] = (T)(img)(0,y,z,c)), \ - (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \ - (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \ - (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[6] = (T)(img)(_n1##x,y,z,c)), \ - (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ - 2>=(img)._width?(img).width() - 1:2); \ - (_n2##x<(img).width() && ( \ - (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[7] = (T)(img)(_n2##x,y,z,c)), \ - (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ - _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], \ - I[4] = I[5], I[5] = I[6], I[6] = I[7], \ - I[8] = I[9], I[9] = I[10], I[10] = I[11], \ - I[12] = I[13], I[13] = I[14], I[14] = I[15], \ - _p1##x = x++, ++_n1##x, ++_n2##x) - -#define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ - _n2##x = (int)( \ - (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[4] = (T)(img)(_p1##x,y,z,c)), \ - (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \ - (I[1] = (T)(img)(x,_p1##y,z,c)), \ - (I[5] = (T)(img)(x,y,z,c)), \ - (I[9] = (T)(img)(x,_n1##y,z,c)), \ - (I[13] = (T)(img)(x,_n2##y,z,c)), \ - (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[6] = (T)(img)(_n1##x,y,z,c)), \ - (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ - x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ - x<=(int)(x1) && ((_n2##x<(img).width() && ( \ - (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[7] = (T)(img)(_n2##x,y,z,c)), \ - (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ - _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], \ - I[4] = I[5], I[5] = I[6], I[6] = I[7], \ - I[8] = I[9], I[9] = I[10], I[10] = I[11], \ - I[12] = I[13], I[13] = I[14], I[14] = I[15], \ - _p1##x = x++, ++_n1##x, ++_n2##x) - -#define cimg_for5x5(img,x,y,z,c,I,T) \ - cimg_for5((img)._height,y) for (int x = 0, \ - _p2##x = 0, _p1##x = 0, \ - _n1##x = 1>=(img)._width?(img).width() - 1:1, \ - _n2##x = (int)( \ - (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ - (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \ - (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \ - (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \ - (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[13] = (T)(img)(_n1##x,y,z,c)), \ - (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ - 2>=(img)._width?(img).width() - 1:2); \ - (_n2##x<(img).width() && ( \ - (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[14] = (T)(img)(_n2##x,y,z,c)), \ - (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ - _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ - I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ - I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ - I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ - I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ - _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) - -#define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _p2##x = x - 2<0?0:x - 2, \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ - _n2##x = (int)( \ - (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ - (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \ - (I[10] = (T)(img)(_p2##x,y,z,c)), \ - (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \ - (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \ - (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ - (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[11] = (T)(img)(_p1##x,y,z,c)), \ - (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \ - (I[2] = (T)(img)(x,_p2##y,z,c)), \ - (I[7] = (T)(img)(x,_p1##y,z,c)), \ - (I[12] = (T)(img)(x,y,z,c)), \ - (I[17] = (T)(img)(x,_n1##y,z,c)), \ - (I[22] = (T)(img)(x,_n2##y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[13] = (T)(img)(_n1##x,y,z,c)), \ - (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ - x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ - x<=(int)(x1) && ((_n2##x<(img).width() && ( \ - (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[14] = (T)(img)(_n2##x,y,z,c)), \ - (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ - _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ - I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ - I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ - I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ - I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ - _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) - -#define cimg_for6x6(img,x,y,z,c,I,T) \ - cimg_for6((img)._height,y) for (int x = 0, \ - _p2##x = 0, _p1##x = 0, \ - _n1##x = 1>=(img)._width?(img).width() - 1:1, \ - _n2##x = 2>=(img)._width?(img).width() - 1:2, \ - _n3##x = (int)( \ - (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ - (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \ - (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \ - (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \ - (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \ - (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[15] = (T)(img)(_n1##x,y,z,c)), \ - (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[16] = (T)(img)(_n2##x,y,z,c)), \ - (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ - 3>=(img)._width?(img).width() - 1:3); \ - (_n3##x<(img).width() && ( \ - (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[17] = (T)(img)(_n3##x,y,z,c)), \ - (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ - _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ - I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ - I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ - I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ - I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ - I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ - _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) - -#define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \ - _p2##x = x - 2<0?0:x - 2, \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ - _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ - _n3##x = (int)( \ - (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ - (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \ - (I[12] = (T)(img)(_p2##x,y,z,c)), \ - (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \ - (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \ - (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \ - (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ - (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[13] = (T)(img)(_p1##x,y,z,c)), \ - (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \ - (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \ - (I[2] = (T)(img)(x,_p2##y,z,c)), \ - (I[8] = (T)(img)(x,_p1##y,z,c)), \ - (I[14] = (T)(img)(x,y,z,c)), \ - (I[20] = (T)(img)(x,_n1##y,z,c)), \ - (I[26] = (T)(img)(x,_n2##y,z,c)), \ - (I[32] = (T)(img)(x,_n3##y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[15] = (T)(img)(_n1##x,y,z,c)), \ - (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[16] = (T)(img)(_n2##x,y,z,c)), \ - (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ - x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ - x<=(int)(x1) && ((_n3##x<(img).width() && ( \ - (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[17] = (T)(img)(_n3##x,y,z,c)), \ - (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ - _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ - I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ - I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ - I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ - I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ - I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ - _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) - -#define cimg_for7x7(img,x,y,z,c,I,T) \ - cimg_for7((img)._height,y) for (int x = 0, \ - _p3##x = 0, _p2##x = 0, _p1##x = 0, \ - _n1##x = 1>=(img)._width?(img).width() - 1:1, \ - _n2##x = 2>=(img)._width?(img).width() - 1:2, \ - _n3##x = (int)( \ - (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ - (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \ - (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \ - (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \ - (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \ - (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \ - (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \ - (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ - (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[25] = (T)(img)(_n1##x,y,z,c)), \ - (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ - (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[26] = (T)(img)(_n2##x,y,z,c)), \ - (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ - 3>=(img)._width?(img).width() - 1:3); \ - (_n3##x<(img).width() && ( \ - (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ - (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[27] = (T)(img)(_n3##x,y,z,c)), \ - (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ - _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ - I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ - I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ - I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ - I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ - I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ - I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ - _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) - -#define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _p3##x = x - 3<0?0:x - 3, \ - _p2##x = x - 2<0?0:x - 2, \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ - _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ - _n3##x = (int)( \ - (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ - (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \ - (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \ - (I[21] = (T)(img)(_p3##x,y,z,c)), \ - (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \ - (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \ - (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \ - (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ - (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \ - (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \ - (I[22] = (T)(img)(_p2##x,y,z,c)), \ - (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \ - (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \ - (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \ - (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ - (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \ - (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[23] = (T)(img)(_p1##x,y,z,c)), \ - (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \ - (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \ - (I[3] = (T)(img)(x,_p3##y,z,c)), \ - (I[10] = (T)(img)(x,_p2##y,z,c)), \ - (I[17] = (T)(img)(x,_p1##y,z,c)), \ - (I[24] = (T)(img)(x,y,z,c)), \ - (I[31] = (T)(img)(x,_n1##y,z,c)), \ - (I[38] = (T)(img)(x,_n2##y,z,c)), \ - (I[45] = (T)(img)(x,_n3##y,z,c)), \ - (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ - (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[25] = (T)(img)(_n1##x,y,z,c)), \ - (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ - (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[26] = (T)(img)(_n2##x,y,z,c)), \ - (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ - x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ - x<=(int)(x1) && ((_n3##x<(img).width() && ( \ - (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ - (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[27] = (T)(img)(_n3##x,y,z,c)), \ - (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ - _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ - I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ - I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ - I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ - I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ - I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ - I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ - _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) - -#define cimg_for8x8(img,x,y,z,c,I,T) \ - cimg_for8((img)._height,y) for (int x = 0, \ - _p3##x = 0, _p2##x = 0, _p1##x = 0, \ - _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ - _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ - _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ - _n4##x = (int)( \ - (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ - (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \ - (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \ - (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \ - (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \ - (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \ - (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \ - (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \ - (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ - (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[28] = (T)(img)(_n1##x,y,z,c)), \ - (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ - (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ - (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[29] = (T)(img)(_n2##x,y,z,c)), \ - (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ - (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ - (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ - (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[30] = (T)(img)(_n3##x,y,z,c)), \ - (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ - (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ - 4>=((img)._width)?(img).width() - 1:4); \ - (_n4##x<(img).width() && ( \ - (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ - (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ - (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ - (I[31] = (T)(img)(_n4##x,y,z,c)), \ - (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ - (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ - (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ - (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ - _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ - I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ - I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ - I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ - I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ - I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ - I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ - I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ - _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) - -#define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _p3##x = x - 3<0?0:x - 3, \ - _p2##x = x - 2<0?0:x - 2, \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ - _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ - _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ - _n4##x = (int)( \ - (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ - (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \ - (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \ - (I[24] = (T)(img)(_p3##x,y,z,c)), \ - (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \ - (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \ - (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \ - (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \ - (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ - (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \ - (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \ - (I[25] = (T)(img)(_p2##x,y,z,c)), \ - (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \ - (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \ - (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \ - (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \ - (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ - (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \ - (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[26] = (T)(img)(_p1##x,y,z,c)), \ - (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \ - (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \ - (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \ - (I[3] = (T)(img)(x,_p3##y,z,c)), \ - (I[11] = (T)(img)(x,_p2##y,z,c)), \ - (I[19] = (T)(img)(x,_p1##y,z,c)), \ - (I[27] = (T)(img)(x,y,z,c)), \ - (I[35] = (T)(img)(x,_n1##y,z,c)), \ - (I[43] = (T)(img)(x,_n2##y,z,c)), \ - (I[51] = (T)(img)(x,_n3##y,z,c)), \ - (I[59] = (T)(img)(x,_n4##y,z,c)), \ - (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ - (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[28] = (T)(img)(_n1##x,y,z,c)), \ - (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ - (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ - (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[29] = (T)(img)(_n2##x,y,z,c)), \ - (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ - (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ - (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ - (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[30] = (T)(img)(_n3##x,y,z,c)), \ - (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ - (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ - x + 4>=(img).width()?(img).width() - 1:x + 4); \ - x<=(int)(x1) && ((_n4##x<(img).width() && ( \ - (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ - (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ - (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ - (I[31] = (T)(img)(_n4##x,y,z,c)), \ - (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ - (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ - (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ - (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ - _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ - I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ - I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ - I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ - I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ - I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ - I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ - I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ - _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) - -#define cimg_for9x9(img,x,y,z,c,I,T) \ - cimg_for9((img)._height,y) for (int x = 0, \ - _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \ - _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ - _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ - _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ - _n4##x = (int)( \ - (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \ - (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \ - (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \ - (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \ - (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \ - (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \ - (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \ - (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \ - (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ - (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ - (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[41] = (T)(img)(_n1##x,y,z,c)), \ - (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ - (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ - (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ - (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[42] = (T)(img)(_n2##x,y,z,c)), \ - (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ - (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ - (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ - (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ - (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[43] = (T)(img)(_n3##x,y,z,c)), \ - (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ - (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ - 4>=((img)._width)?(img).width() - 1:4); \ - (_n4##x<(img).width() && ( \ - (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ - (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ - (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ - (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ - (I[44] = (T)(img)(_n4##x,y,z,c)), \ - (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ - (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ - (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ - (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ - _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ - I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ - I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ - I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ - I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ - I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ - I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ - I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ - I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ - I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ - I[79] = I[80], \ - _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) - -#define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \ - cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _p4##x = x - 4<0?0:x - 4, \ - _p3##x = x - 3<0?0:x - 3, \ - _p2##x = x - 2<0?0:x - 2, \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ - _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ - _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ - _n4##x = (int)( \ - (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \ - (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \ - (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \ - (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \ - (I[36] = (T)(img)(_p4##x,y,z,c)), \ - (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \ - (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \ - (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \ - (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \ - (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \ - (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \ - (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \ - (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \ - (I[37] = (T)(img)(_p3##x,y,z,c)), \ - (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \ - (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \ - (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \ - (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \ - (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \ - (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \ - (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \ - (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \ - (I[38] = (T)(img)(_p2##x,y,z,c)), \ - (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \ - (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \ - (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \ - (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \ - (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \ - (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \ - (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \ - (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[39] = (T)(img)(_p1##x,y,z,c)), \ - (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \ - (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \ - (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \ - (I[4] = (T)(img)(x,_p4##y,z,c)), \ - (I[13] = (T)(img)(x,_p3##y,z,c)), \ - (I[22] = (T)(img)(x,_p2##y,z,c)), \ - (I[31] = (T)(img)(x,_p1##y,z,c)), \ - (I[40] = (T)(img)(x,y,z,c)), \ - (I[49] = (T)(img)(x,_n1##y,z,c)), \ - (I[58] = (T)(img)(x,_n2##y,z,c)), \ - (I[67] = (T)(img)(x,_n3##y,z,c)), \ - (I[76] = (T)(img)(x,_n4##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ - (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ - (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ - (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[41] = (T)(img)(_n1##x,y,z,c)), \ - (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ - (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ - (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ - (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ - (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ - (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ - (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ - (I[42] = (T)(img)(_n2##x,y,z,c)), \ - (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ - (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ - (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ - (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ - (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ - (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ - (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ - (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ - (I[43] = (T)(img)(_n3##x,y,z,c)), \ - (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ - (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ - (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ - (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ - x + 4>=(img).width()?(img).width() - 1:x + 4); \ - x<=(int)(x1) && ((_n4##x<(img).width() && ( \ - (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ - (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ - (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ - (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ - (I[44] = (T)(img)(_n4##x,y,z,c)), \ - (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ - (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ - (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ - (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ - _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ - I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ - I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ - I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ - I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ - I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ - I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ - I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ - I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ - I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ - I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ - I[79] = I[80], \ - _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) - -#define cimg_for2x2x2(img,x,y,z,c,I,T) \ - cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \ - _n1##x = (int)( \ - (I[0] = (T)(img)(0,y,z,c)), \ - (I[2] = (T)(img)(0,_n1##y,z,c)), \ - (I[4] = (T)(img)(0,y,_n1##z,c)), \ - (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \ - 1>=(img)._width?(img).width() - 1:1); \ - (_n1##x<(img).width() && ( \ - (I[1] = (T)(img)(_n1##x,y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ - (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ - x==--_n1##x; \ - I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ - ++x, ++_n1##x) - -#define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ - cimg_for_in2((img)._depth,z0,z1,z) cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _n1##x = (int)( \ - (I[0] = (T)(img)(x,y,z,c)), \ - (I[2] = (T)(img)(x,_n1##y,z,c)), \ - (I[4] = (T)(img)(x,y,_n1##z,c)), \ - (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \ - x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ - x<=(int)(x1) && ((_n1##x<(img).width() && ( \ - (I[1] = (T)(img)(_n1##x,y,z,c)), \ - (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ - (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ - x==--_n1##x); \ - I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ - ++x, ++_n1##x) - -#define cimg_for3x3x3(img,x,y,z,c,I,T) \ - cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \ - _p1##x = 0, \ - _n1##x = (int)( \ - (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ - (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \ - (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \ - (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \ - (I[12] = I[13] = (T)(img)(0,y,z,c)), \ - (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \ - (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \ - (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \ - (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \ - 1>=(img)._width?(img).width() - 1:1); \ - (_n1##x<(img).width() && ( \ - (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ - (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ - (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ - (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[14] = (T)(img)(_n1##x,y,z,c)), \ - (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ - (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ - (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ - x==--_n1##x; \ - I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ - I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ - I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ - _p1##x = x++, ++_n1##x) - -#define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ - cimg_for_in3((img)._depth,z0,z1,z) cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ - _p1##x = x - 1<0?0:x - 1, \ - _n1##x = (int)( \ - (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ - (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \ - (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \ - (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[12] = (T)(img)(_p1##x,y,z,c)), \ - (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \ - (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \ - (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \ - (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \ - (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \ - (I[4] = (T)(img)(x,y,_p1##z,c)), \ - (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \ - (I[10] = (T)(img)(x,_p1##y,z,c)), \ - (I[13] = (T)(img)(x,y,z,c)), \ - (I[16] = (T)(img)(x,_n1##y,z,c)), \ - (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \ - (I[22] = (T)(img)(x,y,_n1##z,c)), \ - (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \ - x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ - x<=(int)(x1) && ((_n1##x<(img).width() && ( \ - (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ - (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ - (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ - (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[14] = (T)(img)(_n1##x,y,z,c)), \ - (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ - (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ - (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ - (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ - x==--_n1##x); \ - I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ - I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ - I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ - _p1##x = x++, ++_n1##x) - -#define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l) -#define cimglist_for_in(list,l0,l1,l) \ - for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \ - l<=_max##l; ++l) - -#define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn - -// Macros used to display error messages when exceptions are thrown. -// You should not use these macros is your own code. -#define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::" -#define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']' -#define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::" -#define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type() -#define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::" -#define cimglist_instance _width,_allocated_width,_data,pixel_type() - -/*------------------------------------------------ - # - # - # Define cimg_library:: namespace - # - # - -------------------------------------------------*/ -//! Contains <i>all classes and functions</i> of the \CImg library. -/** - This namespace is defined to avoid functions and class names collisions - that could happen with the inclusion of other C++ header files. - Anyway, it should not happen often and you should reasonnably start most of your - \CImg-based programs with - \code - #include "CImg.h" - using namespace cimg_library; - \endcode - to simplify the declaration of \CImg Library objects afterwards. -**/ -namespace cimg_library_suffixed { - - // Declare the four classes of the CImg Library. - template<typename T=float> struct CImg; - template<typename T=float> struct CImgList; - struct CImgDisplay; - struct CImgException; - - // Declare cimg:: namespace. - // This is an uncomplete namespace definition here. It only contains some - // necessary stuff to ensure a correct declaration order of the classes and functions - // defined afterwards. - namespace cimg { - - // Define Ascii sequences for colored terminal output. -#ifdef cimg_use_vt100 - static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; - static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; - static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; - static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; - static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; - static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; - static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; - static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; - static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; - static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; - static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; -#else - static const char t_normal[] = { 0 }; - static const char *const t_black = cimg::t_normal, - *const t_red = cimg::t_normal, - *const t_green = cimg::t_normal, - *const t_yellow = cimg::t_normal, - *const t_blue = cimg::t_normal, - *const t_magenta = cimg::t_normal, - *const t_cyan = cimg::t_normal, - *const t_white = cimg::t_normal, - *const t_bold = cimg::t_normal, - *const t_underscore = cimg::t_normal; -#endif - - inline std::FILE* output(std::FILE *file=0); - inline void info(); - - //! Avoid warning messages due to unused parameters. Do nothing actually. - template<typename T> - inline void unused(const T&, ...) {} - - // [internal] Lock/unlock a mutex for managing concurrent threads. - // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }. - // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg. - inline int mutex(const unsigned int n, const int lock_mode=1); - - inline unsigned int& exception_mode(const unsigned int value, const bool is_set) { - static unsigned int mode = cimg_verbosity; - if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } - return mode; - } - - // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. - inline FILE* _stdin(const bool throw_exception=true); - inline FILE* _stdout(const bool throw_exception=true); - inline FILE* _stderr(const bool throw_exception=true); - - // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character - // at the end of the string. -#if cimg_OS==2 && defined(_MSC_VER) - inline int _snprintf(char *const s, const size_t size, const char *const format, ...) { - va_list ap; - va_start(ap,format); - const int result = _vsnprintf(s,size,format,ap); - va_end(ap); - return result; - } - - inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) { - int result = -1; - cimg::mutex(6); - if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap); - if (result==-1) result = _vscprintf(format,ap); - cimg::mutex(6,0); - return result; - } - - // Mutex-protected version of sscanf, sprintf and snprintf. - // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX. -#elif defined(__MACOSX__) || defined(__APPLE__) - inline int _sscanf(const char *const s, const char *const format, ...) { - cimg::mutex(6); - va_list args; - va_start(args,format); - const int result = std::vsscanf(s,format,args); - va_end(args); - cimg::mutex(6,0); - return result; - } - - inline int _sprintf(char *const s, const char *const format, ...) { - cimg::mutex(6); - va_list args; - va_start(args,format); - const int result = std::vsprintf(s,format,args); - va_end(args); - cimg::mutex(6,0); - return result; - } - - inline int _snprintf(char *const s, const size_t n, const char *const format, ...) { - cimg::mutex(6); - va_list args; - va_start(args,format); - const int result = std::vsnprintf(s,n,format,args); - va_end(args); - cimg::mutex(6,0); - return result; - } - - inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) { - cimg::mutex(6); - const int result = std::vsnprintf(s,size,format,ap); - cimg::mutex(6,0); - return result; - } -#endif - - //! Set current \CImg exception mode. - /** - The way error messages are handled by \CImg can be changed dynamically, using this function. - \param mode Desired exception mode. Possible values are: - - \c 0: Hide library messages (quiet mode). - - \c 1: Print library messages on the console. - - \c 2: Display library messages on a dialog window. - - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!). - - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!). - **/ - inline unsigned int& exception_mode(const unsigned int mode) { - return exception_mode(mode,true); - } - - //! Return current \CImg exception mode. - /** - \note By default, return the value of configuration macro \c cimg_verbosity - **/ - inline unsigned int& exception_mode() { - return exception_mode(0,false); - } - - inline unsigned int openmp_mode(const unsigned int value, const bool is_set) { - static unsigned int mode = 2; - if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); } - return mode; - } - - //! Set current \CImg openmp mode. - /** - The way openmp-based methods are handled by \CImg can be changed dynamically, using this function. - \param mode Desired openmp mode. Possible values are: - - \c 0: Never parallelize. - - \c 1: Always parallelize. - - \c 2: Adaptive parallelization mode (default behavior). - **/ - inline unsigned int openmp_mode(const unsigned int mode) { - return openmp_mode(mode,true); - } - - //! Return current \CImg openmp mode. - inline unsigned int openmp_mode() { - return openmp_mode(0,false); - } - -#ifndef cimg_openmp_sizefactor -#define cimg_openmp_sizefactor 1 -#endif -#define cimg_openmp_if(cond) if ((cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond)))) -#define cimg_openmp_if_size(size,min_size) cimg_openmp_if((size)>=(cimg_openmp_sizefactor)*(min_size)) - -#if cimg_OS==2 -// Disable parallelization of simple loops on Windows, due to noticed performance drop. -#define cimg_openmp_for(instance,expr,min_size) cimg_rof((instance),ptr,T) *ptr = (T)(expr); -#else -#define cimg_openmp_for(instance,expr,min_size) \ - cimg_pragma_openmp(parallel for cimg_openmp_if_size((instance).size(),min_size)) \ - cimg_rof((instance),ptr,T) *ptr = (T)(expr); -#endif - - // Display a simple dialog box, and wait for the user's response. - inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK", - const char *const button2_label=0, const char *const button3_label=0, - const char *const button4_label=0, const char *const button5_label=0, - const char *const button6_label=0, const bool centering=false); - - // Evaluate math expression. - inline double eval(const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0); - - } - - /*--------------------------------------- - # - # Define the CImgException structures - # - --------------------------------------*/ - //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call. - /** - \par Overview - - CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException). - CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead. - These classes can be: - - - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal. - This is the only \c non-derived exception class. - - - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid. - This is probably one of the most thrown exception by \CImg. - For instance, the following example throws a \c CImgArgumentException: - \code - CImg<float> img(100,100,1,3); // Define a 100x100 color image with float-valued pixels - img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis - \endcode - - - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances. - - - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit - the function requirements. For instance, the following example throws a \c CImgInstanceException: - \code - const CImg<float> img; // Define an empty image - const float value = img.at(0); // Try to read first pixel value (does not exist) - \endcode - - - \b CImgIOException: Thrown when an error occured when trying to load or save image files. - This happens when trying to read files that do not exist or with invalid formats. - For instance, the following example throws a \c CImgIOException: - \code - const CImg<float> img("missing_file.jpg"); // Try to load a file that does not exist - \endcode - - - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and - when a \CImg function has to display a warning message (see cimg::warn()). - - It is not recommended to throw CImgException instances by yourself, - since they are expected to be thrown only by \CImg. - When an error occurs in a library function call, \CImg may display error messages on the screen or on the - standard output, depending on the current \CImg exception mode. - The \CImg exception mode can be get and set by functions cimg::exception_mode() and - cimg::exception_mode(unsigned int). - - \par Exceptions handling - - In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown. - This may lead the program to break (this is the default behavior), but you can bypass this behavior by - handling the exceptions by yourself, - using a usual <tt>try { ... } catch () { ... }</tt> bloc, as in the following example: - \code - #define "CImg.h" - using namespace cimg_library; - int main() { - cimg::exception_mode(0); // Enable quiet exception mode - try { - ... // Here, do what you want to stress CImg - } catch (CImgException& e) { // You succeeded: something went wrong! - std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message - ... // Do what you want now to save the ship! - } - } - \endcode - **/ - struct CImgException : public std::exception { -#define _cimg_exception_err(etype,disp_flag) \ - std::va_list ap, ap2; \ - va_start(ap,format); va_start(ap2,format); \ - int size = cimg_vsnprintf(0,0,format,ap2); \ - if (size++>=0) { \ - delete[] _message; \ - _message = new char[size]; \ - cimg_vsnprintf(_message,size,format,ap); \ - if (cimg::exception_mode()) { \ - std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \ - if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \ - catch (CImgException&) {} \ - if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \ - } \ - } \ - va_end(ap); va_end(ap2); \ - - char *_message; - CImgException() { _message = new char[1]; *_message = 0; } - CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); } - CImgException(const CImgException& e):std::exception(e) { - const size_t size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - } - ~CImgException() throw() { delete[] _message; } - CImgException& operator=(const CImgException& e) { - const size_t size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - return *this; - } - //! Return a C-string containing the error message associated to the thrown exception. - const char *what() const throw() { return _message; } - }; - - // The CImgAbortException class is used to throw an exception when - // a computationally-intensive function has been aborted by an external signal. - struct CImgAbortException : public std::exception { - char *_message; - CImgAbortException() { _message = new char[1]; *_message = 0; } - CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); } - CImgAbortException(const CImgAbortException& e):std::exception(e) { - const size_t size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - } - ~CImgAbortException() throw() { delete[] _message; } - CImgAbortException& operator=(const CImgAbortException& e) { - const size_t size = std::strlen(e._message); - _message = new char[size + 1]; - std::strncpy(_message,e._message,size); - _message[size] = 0; - return *this; - } - //! Return a C-string containing the error message associated to the thrown exception. - const char *what() const throw() { return _message; } - }; - - // The CImgArgumentException class is used to throw an exception related - // to invalid arguments encountered in a library function call. - struct CImgArgumentException : public CImgException { - CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); } - }; - - // The CImgDisplayException class is used to throw an exception related - // to display problems encountered in a library function call. - struct CImgDisplayException : public CImgException { - CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); } - }; - - // The CImgInstanceException class is used to throw an exception related - // to an invalid instance encountered in a library function call. - struct CImgInstanceException : public CImgException { - CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } - }; - - // The CImgIOException class is used to throw an exception related - // to input/output file problems encountered in a library function call. - struct CImgIOException : public CImgException { - CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); } - }; - - // The CImgWarningException class is used to throw an exception for warnings - // encountered in a library function call. - struct CImgWarningException : public CImgException { - CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); } - }; - - /*------------------------------------- - # - # Define cimg:: namespace - # - -----------------------------------*/ - //! Contains \a low-level functions and variables of the \CImg Library. - /** - Most of the functions and variables within this namespace are used by the \CImg library for low-level operations. - You may use them to access specific const values or environment variables internally used by \CImg. - \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code. Lot of functions in the - <tt>cimg:: namespace</tt> have the same names as standard C functions that may be defined in the global - namespace <tt>::</tt>. - **/ - namespace cimg { - - // Define traits that will be used to determine the best data type to work in CImg functions. - // - template<typename T> struct type { - static const char* string() { - static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", - "unknown32", "unknown40", "unknown48", "unknown56", - "unknown64", "unknown72", "unknown80", "unknown88", - "unknown96", "unknown104", "unknown112", "unknown120", - "unknown128" }; - return s[(sizeof(T)<17)?sizeof(T):0]; - } - static bool is_float() { return false; } - static bool is_inf(const T) { return false; } - static bool is_nan(const T) { return false; } - static T min() { return ~max(); } - static T max() { return (T)1<<(8*sizeof(T) - 1); } - static T inf() { return max(); } - static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; } - static const char* format() { return "%s"; } - static const char* format_s() { return "%s"; } - static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; } - }; - - template<> struct type<bool> { - static const char* string() { static const char *const s = "bool"; return s; } - static bool is_float() { return false; } - static bool is_inf(const bool) { return false; } - static bool is_nan(const bool) { return false; } - static bool min() { return false; } - static bool max() { return true; } - static bool inf() { return max(); } - static bool is_inf() { return false; } - static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; } - static const char* format() { return "%s"; } - static const char* format_s() { return "%s"; } - static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } - }; - - template<> struct type<unsigned char> { - static const char* string() { static const char *const s = "unsigned char"; return s; } - static bool is_float() { return false; } - static bool is_inf(const unsigned char) { return false; } - static bool is_nan(const unsigned char) { return false; } - static unsigned char min() { return 0; } - static unsigned char max() { return (unsigned char)-1; } - static unsigned char inf() { return max(); } - static unsigned char cut(const double val) { - return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } - static const char* format() { return "%u"; } - static const char* format_s() { return "%u"; } - static unsigned int format(const unsigned char val) { return (unsigned int)val; } - }; - -#if defined(CHAR_MAX) && CHAR_MAX==255 - template<> struct type<char> { - static const char* string() { static const char *const s = "char"; return s; } - static bool is_float() { return false; } - static bool is_inf(const char) { return false; } - static bool is_nan(const char) { return false; } - static char min() { return 0; } - static char max() { return (char)-1; } - static char inf() { return max(); } - static char cut(const double val) { - return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } - static const char* format() { return "%u"; } - static const char* format_s() { return "%u"; } - static unsigned int format(const char val) { return (unsigned int)val; } - }; -#else - template<> struct type<char> { - static const char* string() { static const char *const s = "char"; return s; } - static bool is_float() { return false; } - static bool is_inf(const char) { return false; } - static bool is_nan(const char) { return false; } - static char min() { return ~max(); } - static char max() { return (char)((unsigned char)-1>>1); } - static char inf() { return max(); } - static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; } - static const char* format() { return "%d"; } - static const char* format_s() { return "%d"; } - static int format(const char val) { return (int)val; } - }; -#endif - - template<> struct type<signed char> { - static const char* string() { static const char *const s = "signed char"; return s; } - static bool is_float() { return false; } - static bool is_inf(const signed char) { return false; } - static bool is_nan(const signed char) { return false; } - static signed char min() { return ~max(); } - static signed char max() { return (signed char)((unsigned char)-1>>1); } - static signed char inf() { return max(); } - static signed char cut(const double val) { - return val<(double)min()?min():val>(double)max()?max():(signed char)val; } - static const char* format() { return "%d"; } - static const char* format_s() { return "%d"; } - static int format(const signed char val) { return (int)val; } - }; - - template<> struct type<unsigned short> { - static const char* string() { static const char *const s = "unsigned short"; return s; } - static bool is_float() { return false; } - static bool is_inf(const unsigned short) { return false; } - static bool is_nan(const unsigned short) { return false; } - static unsigned short min() { return 0; } - static unsigned short max() { return (unsigned short)-1; } - static unsigned short inf() { return max(); } - static unsigned short cut(const double val) { - return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } - static const char* format() { return "%u"; } - static const char* format_s() { return "%u"; } - static unsigned int format(const unsigned short val) { return (unsigned int)val; } - }; - - template<> struct type<short> { - static const char* string() { static const char *const s = "short"; return s; } - static bool is_float() { return false; } - static bool is_inf(const short) { return false; } - static bool is_nan(const short) { return false; } - static short min() { return ~max(); } - static short max() { return (short)((unsigned short)-1>>1); } - static short inf() { return max(); } - static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; } - static const char* format() { return "%d"; } - static const char* format_s() { return "%d"; } - static int format(const short val) { return (int)val; } - }; - - template<> struct type<unsigned int> { - static const char* string() { static const char *const s = "unsigned int"; return s; } - static bool is_float() { return false; } - static bool is_inf(const unsigned int) { return false; } - static bool is_nan(const unsigned int) { return false; } - static unsigned int min() { return 0; } - static unsigned int max() { return (unsigned int)-1; } - static unsigned int inf() { return max(); } - static unsigned int cut(const double val) { - return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } - static const char* format() { return "%u"; } - static const char* format_s() { return "%u"; } - static unsigned int format(const unsigned int val) { return val; } - }; - - template<> struct type<int> { - static const char* string() { static const char *const s = "int"; return s; } - static bool is_float() { return false; } - static bool is_inf(const int) { return false; } - static bool is_nan(const int) { return false; } - static int min() { return ~max(); } - static int max() { return (int)((unsigned int)-1>>1); } - static int inf() { return max(); } - static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } - static const char* format() { return "%d"; } - static const char* format_s() { return "%d"; } - static int format(const int val) { return val; } - }; - - template<> struct type<cimg_uint64> { - static const char* string() { static const char *const s = "unsigned int64"; return s; } - static bool is_float() { return false; } - static bool is_inf(const cimg_uint64) { return false; } - static bool is_nan(const cimg_uint64) { return false; } - static cimg_uint64 min() { return 0; } - static cimg_uint64 max() { return (cimg_uint64)-1; } - static cimg_uint64 inf() { return max(); } - static cimg_uint64 cut(const double val) { - return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } - static const char* format() { return cimg_fuint64; } - static const char* format_s() { return cimg_fuint64; } - static unsigned long format(const cimg_uint64 val) { return (unsigned long)val; } - }; - - template<> struct type<cimg_int64> { - static const char* string() { static const char *const s = "int64"; return s; } - static bool is_float() { return false; } - static bool is_inf(const cimg_int64) { return false; } - static bool is_nan(const cimg_int64) { return false; } - static cimg_int64 min() { return ~max(); } - static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); } - static cimg_int64 inf() { return max(); } - static cimg_int64 cut(const double val) { - return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; - } - static const char* format() { return cimg_fint64; } - static const char* format_s() { return cimg_fint64; } - static long format(const long val) { return (long)val; } - }; - - template<> struct type<double> { - static const char* string() { static const char *const s = "double"; return s; } - static bool is_float() { return true; } - static bool is_inf(const double val) { -#ifdef isinf - return (bool)isinf(val); -#else - return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max()); -#endif - } - static bool is_nan(const double val) { // Custom version that works with '-ffast-math' - if (sizeof(double)==8) { - cimg_uint64 u; - std::memcpy(&u,&val,sizeof(double)); - return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000; - } -#ifdef isnan - return (bool)isnan(val); -#else - return !(val==val); -#endif - } - static double min() { return -DBL_MAX; } - static double max() { return DBL_MAX; } - static double inf() { -#ifdef INFINITY - return (double)INFINITY; -#else - return max()*max(); -#endif - } - static double nan() { -#ifdef NAN - return (double)NAN; -#else - const double val_nan = -std::sqrt(-1.); return val_nan; -#endif - } - static double cut(const double val) { return val; } - static const char* format() { return "%.17g"; } - static const char* format_s() { return "%g"; } - static double format(const double val) { return val; } - }; - - template<> struct type<float> { - static const char* string() { static const char *const s = "float"; return s; } - static bool is_float() { return true; } - static bool is_inf(const float val) { -#ifdef isinf - return (bool)isinf(val); -#else - return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max()); -#endif - } - static bool is_nan(const float val) { // Custom version that works with '-ffast-math' - if (sizeof(float)==4) { - unsigned int u; - std::memcpy(&u,&val,sizeof(float)); - return (u&0x7fffffff)>0x7f800000; - } -#ifdef isnan - return (bool)isnan(val); -#else - return !(val==val); -#endif - } - static float min() { return -FLT_MAX; } - static float max() { return FLT_MAX; } - static float inf() { return (float)cimg::type<double>::inf(); } - static float nan() { return (float)cimg::type<double>::nan(); } - static float cut(const double val) { return (float)val; } - static float cut(const float val) { return (float)val; } - static const char* format() { return "%.9g"; } - static const char* format_s() { return "%g"; } - static double format(const float val) { return (double)val; } - }; - - template<> struct type<long double> { - static const char* string() { static const char *const s = "long double"; return s; } - static bool is_float() { return true; } - static bool is_inf(const long double val) { -#ifdef isinf - return (bool)isinf(val); -#else - return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max()); -#endif - } - static bool is_nan(const long double val) { -#ifdef isnan - return (bool)isnan(val); -#else - return !(val==val); -#endif - } - static long double min() { return -LDBL_MAX; } - static long double max() { return LDBL_MAX; } - static long double inf() { return max()*max(); } - static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; } - static long double cut(const long double val) { return val; } - static const char* format() { return "%.17g"; } - static const char* format_s() { return "%g"; } - static double format(const long double val) { return (double)val; } - }; - -#ifdef cimg_use_half - template<> struct type<half> { - static const char* string() { static const char *const s = "half"; return s; } - static bool is_float() { return true; } - static bool is_inf(const long double val) { -#ifdef isinf - return (bool)isinf(val); -#else - return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max()); -#endif - } - static bool is_nan(const half val) { // Custom version that works with '-ffast-math' - if (sizeof(half)==2) { - short u; - std::memcpy(&u,&val,sizeof(short)); - return (bool)((u&0x7fff)>0x7c00); - } - return cimg::type<float>::is_nan((float)val); - } - static half min() { return (half)-65504; } - static half max() { return (half)65504; } - static half inf() { return max()*max(); } - static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; } - static half cut(const double val) { return (half)val; } - static const char* format() { return "%.9g"; } - static const char* format_s() { return "%g"; } - static double format(const half val) { return (double)val; } - }; -#endif - - template<typename T, typename t> struct superset { typedef T type; }; - template<> struct superset<bool,unsigned char> { typedef unsigned char type; }; - template<> struct superset<bool,char> { typedef char type; }; - template<> struct superset<bool,signed char> { typedef signed char type; }; - template<> struct superset<bool,unsigned short> { typedef unsigned short type; }; - template<> struct superset<bool,short> { typedef short type; }; - template<> struct superset<bool,unsigned int> { typedef unsigned int type; }; - template<> struct superset<bool,int> { typedef int type; }; - template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; }; - template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<bool,float> { typedef float type; }; - template<> struct superset<bool,double> { typedef double type; }; - template<> struct superset<unsigned char,char> { typedef short type; }; - template<> struct superset<unsigned char,signed char> { typedef short type; }; - template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; }; - template<> struct superset<unsigned char,short> { typedef short type; }; - template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; }; - template<> struct superset<unsigned char,int> { typedef int type; }; - template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; }; - template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<unsigned char,float> { typedef float type; }; - template<> struct superset<unsigned char,double> { typedef double type; }; - template<> struct superset<signed char,unsigned char> { typedef short type; }; - template<> struct superset<signed char,char> { typedef short type; }; - template<> struct superset<signed char,unsigned short> { typedef int type; }; - template<> struct superset<signed char,short> { typedef short type; }; - template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; }; - template<> struct superset<signed char,int> { typedef int type; }; - template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; }; - template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<signed char,float> { typedef float type; }; - template<> struct superset<signed char,double> { typedef double type; }; - template<> struct superset<char,unsigned char> { typedef short type; }; - template<> struct superset<char,signed char> { typedef short type; }; - template<> struct superset<char,unsigned short> { typedef int type; }; - template<> struct superset<char,short> { typedef short type; }; - template<> struct superset<char,unsigned int> { typedef cimg_int64 type; }; - template<> struct superset<char,int> { typedef int type; }; - template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; }; - template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<char,float> { typedef float type; }; - template<> struct superset<char,double> { typedef double type; }; - template<> struct superset<unsigned short,char> { typedef int type; }; - template<> struct superset<unsigned short,signed char> { typedef int type; }; - template<> struct superset<unsigned short,short> { typedef int type; }; - template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; }; - template<> struct superset<unsigned short,int> { typedef int type; }; - template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; }; - template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<unsigned short,float> { typedef float type; }; - template<> struct superset<unsigned short,double> { typedef double type; }; - template<> struct superset<short,unsigned short> { typedef int type; }; - template<> struct superset<short,unsigned int> { typedef cimg_int64 type; }; - template<> struct superset<short,int> { typedef int type; }; - template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; }; - template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<short,float> { typedef float type; }; - template<> struct superset<short,double> { typedef double type; }; - template<> struct superset<unsigned int,char> { typedef cimg_int64 type; }; - template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; }; - template<> struct superset<unsigned int,short> { typedef cimg_int64 type; }; - template<> struct superset<unsigned int,int> { typedef cimg_int64 type; }; - template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; }; - template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<unsigned int,float> { typedef float type; }; - template<> struct superset<unsigned int,double> { typedef double type; }; - template<> struct superset<int,unsigned int> { typedef cimg_int64 type; }; - template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; }; - template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<int,float> { typedef float type; }; - template<> struct superset<int,double> { typedef double type; }; - template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; }; - template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; }; - template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; }; - template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; }; - template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; }; - template<> struct superset<cimg_uint64,float> { typedef double type; }; - template<> struct superset<cimg_uint64,double> { typedef double type; }; - template<> struct superset<cimg_int64,float> { typedef double type; }; - template<> struct superset<cimg_int64,double> { typedef double type; }; - template<> struct superset<float,double> { typedef double type; }; -#ifdef cimg_use_half - template<> struct superset<half,unsigned short> { typedef float type; }; - template<> struct superset<half,short> { typedef float type; }; - template<> struct superset<half,unsigned int> { typedef float type; }; - template<> struct superset<half,int> { typedef float type; }; - template<> struct superset<half,cimg_uint64> { typedef float type; }; - template<> struct superset<half,cimg_int64> { typedef float type; }; - template<> struct superset<half,float> { typedef float type; }; - template<> struct superset<half,double> { typedef double type; }; -#endif - - template<typename t1, typename t2, typename t3> struct superset2 { - typedef typename superset<t1, typename superset<t2,t3>::type>::type type; - }; - - template<typename t1, typename t2, typename t3, typename t4> struct superset3 { - typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type; - }; - - template<typename t1, typename t2> struct last { typedef t2 type; }; - -#define _cimg_Tt typename cimg::superset<T,t>::type -#define _cimg_Tfloat typename cimg::superset<T,float>::type -#define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type -#define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type - - // Define variables used internally by CImg. -#if cimg_display==1 - struct X11_info { - unsigned int nb_wins; - pthread_t *events_thread; - pthread_cond_t wait_event; - pthread_mutex_t wait_event_mutex; - CImgDisplay **wins; - Display *display; - unsigned int nb_bits; - bool is_blue_first; - bool is_shm_enabled; - bool byte_order; -#ifdef cimg_use_xrandr - XRRScreenSize *resolutions; - Rotation curr_rotation; - unsigned int curr_resolution; - unsigned int nb_resolutions; -#endif - X11_info():nb_wins(0),events_thread(0),display(0), - nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) { -#ifdef __FreeBSD__ - XInitThreads(); -#endif - wins = new CImgDisplay*[1024]; - pthread_mutex_init(&wait_event_mutex,0); - pthread_cond_init(&wait_event,0); -#ifdef cimg_use_xrandr - resolutions = 0; - curr_rotation = 0; - curr_resolution = nb_resolutions = 0; -#endif - } - - ~X11_info() { - delete[] wins; - /* - if (events_thread) { - pthread_cancel(*events_thread); - delete events_thread; - } - if (display) { } // XCloseDisplay(display); } - pthread_cond_destroy(&wait_event); - pthread_mutex_unlock(&wait_event_mutex); - pthread_mutex_destroy(&wait_event_mutex); - */ - } - }; -#if defined(cimg_module) - X11_info& X11_attr(); -#elif defined(cimg_main) - X11_info& X11_attr() { static X11_info val; return val; } -#else - inline X11_info& X11_attr() { static X11_info val; return val; } -#endif - -#elif cimg_display==2 - struct Win32_info { - HANDLE wait_event; - Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); } - }; -#if defined(cimg_module) - Win32_info& Win32_attr(); -#elif defined(cimg_main) - Win32_info& Win32_attr() { static Win32_info val; return val; } -#else - inline Win32_info& Win32_attr() { static Win32_info val; return val; } -#endif -#endif -#define cimg_lock_display() cimg::mutex(15) -#define cimg_unlock_display() cimg::mutex(15,0) - - struct Mutex_info { -#ifdef _PTHREAD_H - pthread_mutex_t mutex[32]; - Mutex_info() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); } - void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); } - void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); } - int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); } -#elif cimg_OS==2 - HANDLE mutex[32]; - Mutex_info() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE,0); } - void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); } - void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); } - int trylock(const unsigned int) { return 0; } -#else - Mutex_info() {} - void lock(const unsigned int) {} - void unlock(const unsigned int) {} - int trylock(const unsigned int) { return 0; } -#endif - }; -#if defined(cimg_module) - Mutex_info& Mutex_attr(); -#elif defined(cimg_main) - Mutex_info& Mutex_attr() { static Mutex_info val; return val; } -#else - inline Mutex_info& Mutex_attr() { static Mutex_info val; return val; } -#endif - -#if defined(cimg_use_magick) - static struct Magick_info { - Magick_info() { - Magick::InitializeMagick(""); - } - } _Magick_info; -#endif - -#if cimg_display==1 - // Define keycodes for X11-based graphical systems. - const unsigned int keyESC = XK_Escape; - const unsigned int keyF1 = XK_F1; - const unsigned int keyF2 = XK_F2; - const unsigned int keyF3 = XK_F3; - const unsigned int keyF4 = XK_F4; - const unsigned int keyF5 = XK_F5; - const unsigned int keyF6 = XK_F6; - const unsigned int keyF7 = XK_F7; - const unsigned int keyF8 = XK_F8; - const unsigned int keyF9 = XK_F9; - const unsigned int keyF10 = XK_F10; - const unsigned int keyF11 = XK_F11; - const unsigned int keyF12 = XK_F12; - const unsigned int keyPAUSE = XK_Pause; - const unsigned int key1 = XK_1; - const unsigned int key2 = XK_2; - const unsigned int key3 = XK_3; - const unsigned int key4 = XK_4; - const unsigned int key5 = XK_5; - const unsigned int key6 = XK_6; - const unsigned int key7 = XK_7; - const unsigned int key8 = XK_8; - const unsigned int key9 = XK_9; - const unsigned int key0 = XK_0; - const unsigned int keyBACKSPACE = XK_BackSpace; - const unsigned int keyINSERT = XK_Insert; - const unsigned int keyHOME = XK_Home; - const unsigned int keyPAGEUP = XK_Page_Up; - const unsigned int keyTAB = XK_Tab; - const unsigned int keyQ = XK_q; - const unsigned int keyW = XK_w; - const unsigned int keyE = XK_e; - const unsigned int keyR = XK_r; - const unsigned int keyT = XK_t; - const unsigned int keyY = XK_y; - const unsigned int keyU = XK_u; - const unsigned int keyI = XK_i; - const unsigned int keyO = XK_o; - const unsigned int keyP = XK_p; - const unsigned int keyDELETE = XK_Delete; - const unsigned int keyEND = XK_End; - const unsigned int keyPAGEDOWN = XK_Page_Down; - const unsigned int keyCAPSLOCK = XK_Caps_Lock; - const unsigned int keyA = XK_a; - const unsigned int keyS = XK_s; - const unsigned int keyD = XK_d; - const unsigned int keyF = XK_f; - const unsigned int keyG = XK_g; - const unsigned int keyH = XK_h; - const unsigned int keyJ = XK_j; - const unsigned int keyK = XK_k; - const unsigned int keyL = XK_l; - const unsigned int keyENTER = XK_Return; - const unsigned int keySHIFTLEFT = XK_Shift_L; - const unsigned int keyZ = XK_z; - const unsigned int keyX = XK_x; - const unsigned int keyC = XK_c; - const unsigned int keyV = XK_v; - const unsigned int keyB = XK_b; - const unsigned int keyN = XK_n; - const unsigned int keyM = XK_m; - const unsigned int keySHIFTRIGHT = XK_Shift_R; - const unsigned int keyARROWUP = XK_Up; - const unsigned int keyCTRLLEFT = XK_Control_L; - const unsigned int keyAPPLEFT = XK_Super_L; - const unsigned int keyALT = XK_Alt_L; - const unsigned int keySPACE = XK_space; - const unsigned int keyALTGR = XK_Alt_R; - const unsigned int keyAPPRIGHT = XK_Super_R; - const unsigned int keyMENU = XK_Menu; - const unsigned int keyCTRLRIGHT = XK_Control_R; - const unsigned int keyARROWLEFT = XK_Left; - const unsigned int keyARROWDOWN = XK_Down; - const unsigned int keyARROWRIGHT = XK_Right; - const unsigned int keyPAD0 = XK_KP_0; - const unsigned int keyPAD1 = XK_KP_1; - const unsigned int keyPAD2 = XK_KP_2; - const unsigned int keyPAD3 = XK_KP_3; - const unsigned int keyPAD4 = XK_KP_4; - const unsigned int keyPAD5 = XK_KP_5; - const unsigned int keyPAD6 = XK_KP_6; - const unsigned int keyPAD7 = XK_KP_7; - const unsigned int keyPAD8 = XK_KP_8; - const unsigned int keyPAD9 = XK_KP_9; - const unsigned int keyPADADD = XK_KP_Add; - const unsigned int keyPADSUB = XK_KP_Subtract; - const unsigned int keyPADMUL = XK_KP_Multiply; - const unsigned int keyPADDIV = XK_KP_Divide; - -#elif cimg_display==2 - // Define keycodes for Windows. - const unsigned int keyESC = VK_ESCAPE; - const unsigned int keyF1 = VK_F1; - const unsigned int keyF2 = VK_F2; - const unsigned int keyF3 = VK_F3; - const unsigned int keyF4 = VK_F4; - const unsigned int keyF5 = VK_F5; - const unsigned int keyF6 = VK_F6; - const unsigned int keyF7 = VK_F7; - const unsigned int keyF8 = VK_F8; - const unsigned int keyF9 = VK_F9; - const unsigned int keyF10 = VK_F10; - const unsigned int keyF11 = VK_F11; - const unsigned int keyF12 = VK_F12; - const unsigned int keyPAUSE = VK_PAUSE; - const unsigned int key1 = '1'; - const unsigned int key2 = '2'; - const unsigned int key3 = '3'; - const unsigned int key4 = '4'; - const unsigned int key5 = '5'; - const unsigned int key6 = '6'; - const unsigned int key7 = '7'; - const unsigned int key8 = '8'; - const unsigned int key9 = '9'; - const unsigned int key0 = '0'; - const unsigned int keyBACKSPACE = VK_BACK; - const unsigned int keyINSERT = VK_INSERT; - const unsigned int keyHOME = VK_HOME; - const unsigned int keyPAGEUP = VK_PRIOR; - const unsigned int keyTAB = VK_TAB; - const unsigned int keyQ = 'Q'; - const unsigned int keyW = 'W'; - const unsigned int keyE = 'E'; - const unsigned int keyR = 'R'; - const unsigned int keyT = 'T'; - const unsigned int keyY = 'Y'; - const unsigned int keyU = 'U'; - const unsigned int keyI = 'I'; - const unsigned int keyO = 'O'; - const unsigned int keyP = 'P'; - const unsigned int keyDELETE = VK_DELETE; - const unsigned int keyEND = VK_END; - const unsigned int keyPAGEDOWN = VK_NEXT; - const unsigned int keyCAPSLOCK = VK_CAPITAL; - const unsigned int keyA = 'A'; - const unsigned int keyS = 'S'; - const unsigned int keyD = 'D'; - const unsigned int keyF = 'F'; - const unsigned int keyG = 'G'; - const unsigned int keyH = 'H'; - const unsigned int keyJ = 'J'; - const unsigned int keyK = 'K'; - const unsigned int keyL = 'L'; - const unsigned int keyENTER = VK_RETURN; - const unsigned int keySHIFTLEFT = VK_SHIFT; - const unsigned int keyZ = 'Z'; - const unsigned int keyX = 'X'; - const unsigned int keyC = 'C'; - const unsigned int keyV = 'V'; - const unsigned int keyB = 'B'; - const unsigned int keyN = 'N'; - const unsigned int keyM = 'M'; - const unsigned int keySHIFTRIGHT = VK_SHIFT; - const unsigned int keyARROWUP = VK_UP; - const unsigned int keyCTRLLEFT = VK_CONTROL; - const unsigned int keyAPPLEFT = VK_LWIN; - const unsigned int keyALT = VK_LMENU; - const unsigned int keySPACE = VK_SPACE; - const unsigned int keyALTGR = VK_CONTROL; - const unsigned int keyAPPRIGHT = VK_RWIN; - const unsigned int keyMENU = VK_APPS; - const unsigned int keyCTRLRIGHT = VK_CONTROL; - const unsigned int keyARROWLEFT = VK_LEFT; - const unsigned int keyARROWDOWN = VK_DOWN; - const unsigned int keyARROWRIGHT = VK_RIGHT; - const unsigned int keyPAD0 = 0x60; - const unsigned int keyPAD1 = 0x61; - const unsigned int keyPAD2 = 0x62; - const unsigned int keyPAD3 = 0x63; - const unsigned int keyPAD4 = 0x64; - const unsigned int keyPAD5 = 0x65; - const unsigned int keyPAD6 = 0x66; - const unsigned int keyPAD7 = 0x67; - const unsigned int keyPAD8 = 0x68; - const unsigned int keyPAD9 = 0x69; - const unsigned int keyPADADD = VK_ADD; - const unsigned int keyPADSUB = VK_SUBTRACT; - const unsigned int keyPADMUL = VK_MULTIPLY; - const unsigned int keyPADDIV = VK_DIVIDE; - -#else - // Define random keycodes when no display is available. - // (should rarely be used then!). - const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent) - const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent) - const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent) - const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent) - const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent) - const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent) - const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent) - const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent) - const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent) - const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent) - const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent) - const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent) - const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent) - const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent) - const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent) - const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent) - const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent) - const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent) - const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent) - const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent) - const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent) - const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent) - const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent) - const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent) - const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent) - const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent) - const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent) - const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent) - const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent) - const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent) - const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent) - const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent) - const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent) - const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent) - const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent) - const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent) - const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent) - const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent) - const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent) - const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent) - const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent) - const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent) - const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent) - const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent) - const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent) - const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent) - const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent) - const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent) - const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent) - const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent) - const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent) - const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent) - const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent) - const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent) - const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent) - const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent) - const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent) - const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent) - const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent) - const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent) - const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent) - const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent) - const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent) - const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent) - const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent) - const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent) - const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent) - const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent) - const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent) - const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent) - const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent) - const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent) - const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent) - const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent) - const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent) - const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent) - const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent) - const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent) - const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent) - const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent) - const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent) - const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent) - const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent) - const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent) - const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent) - const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent) - const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent) - const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent) -#endif - - const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI - - // Define a 12x13 binary font (small sans). - static const char *const data_font_small[] = { - " UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw Mw (wnwnwuwpwuypwuwoy" - "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y Hw cwnw >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w " - "ry qw %wuw !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow" - "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq" - "ws}qwbwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwdz" - "\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuypwu" - "woyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw rw" - "swewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwsw<wrwowrwuwqwrwqwswrwswpwmwmwrwswrwowlwq" - "wtwownxsxsxswqwswqwswqwswrwswqwrwowpwrwrwqwtwswswswswqwswmwpwbwoxuxSw_wfwdwYwkw(w0wmwmwGwtwoxnwNw uwswpwuwpwmwm" - "wswq{rwrwrwtwtwrwswfydwdyZwnwtwrwqwrwswowowdwrwqxuwSwrwfwuwnwlwnw[yuw[wowtwgwswqwswqwswewuwowuwowuwowuwowuwnwow" - "uwowswqwmwmwmwjwmwnwmwowswrxswqwswqwswqwswqwswqwswrwrwqwswrwrwrwrwrwrwrwrwqwswqzpwtw #w DwPwtwtwswqwswuwuwuwsws" - "wuwswqwGwqxtwf{qzr~r{qzqwrwpxowtwrw rzcwnwuwq}rwuwqwtwuwqwtwmwnwlwnynwOwowswowkwmwpwuwpwmwjwpwswqwswowmwjwiwjxs" - "wsytwrwuwqwrwrwmwrwqwmwnwmwrwowlwqwuwnwnxsxswuwtwrwqwrwswrwqwswswqwjwpwrwqwswrwtwtwqwuwowuwmwowbwpxsx]ypzoyozpy" - "pzozqznwmwowtwnwqzuyrzoypzozqwuxoypzpwswrwrwrwtwtwswrwrwrwq{owmwmwQyuwqwtwmwoxnypzqxswowowswqwswqwtxr|rwtwtwqyp" - "{q{qwswpwuwownwnwqwsxuwuxswrwrwtwtwswqwrwmwuwuwnwnwowtwpwuwuwewnzpwn{pwuwnwnxgwtxtwrwtwowtw_wuytwgynwmwlwgwswpy" - "uw[wowtwqwtwpwtwpwtwowuwmwnwuwowuwowuwowuwowuwowuwqxuwpwlwmwmwmwjwmwnwmwowrwswuwtwrwqwswqwswqwswqwswqwrwtwqwswu" - "wswrwrwrwrwrwrwrwpwuwpwswqwuwnyoyoyoyoyoyqyuyqyoyoyoyoymwqwjwmwnypzoyoyoyoyoynwnzqwswqwswqwswqwswrwrwqzqwrw^}s}" - "swtwtwswtwtwswtwtwK}rwuwe{s~t~s}rwtwqwrwpxowtwrw qwawewtwpwuwpxuwpycwlwnynwOwowswowkwpypwtwpzpzmwoypwsw[yr}rymw" - "rwtwtwtwrwuwq{qwmwrwq{q{rwm|owlwqxmwnwuwuwuwswuwtwrwqwrwswrwqwswswqylwpwrwqwswrwuwuwuwpwmwmwnwbwMwqwswqwmwswqws" - "wpwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqxnwswpwnwswrwrwrwtwtwrwtwqwrwmwqxlwlx]xuxrwtyqwuwlwpwtwpwmwswqwtxpx" - "owswrwqwswtwuxrwtwqwtwtwrwswrwswnwo{pwuwnxpwnwqwswtwtwswrwrwtwtwswuyuwswjwkwowpwrwowcwowuwnwnwswqxuxowowtwhwuwr" - "wrzpwtwq}jwuwtwuw_}qyoxfwswpyuwowdyoxowtwryuwqyuwqyuwmwnwuwowuwowuwowuwowuwowuwqwt{twl{q{q{q{nwmwnwmwpztwswuwtw" - "rwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwowowswqwuwkwmwmwmwmwmwowswswmwswqwswqwswqwswnwqwjwmwowswqwswqwswqws" - "wqwswqwswqwswgwtxqwswqwswqwswqwswrwrwqwswrwrw^wtwtwswqwswuwuwuwswuwswswqwHwowuwf}t~s|r}swrwrwrwqwtwpwtwr~#zcwew" - "twoynwuxtwtwswgwlwowuwuwr}gyexowswowlwlwrwswlwqwswowowswpz^yayqwqwtwtwuwrwswrwrwrwmwrwqwmwnwsyswrwowlwqwuwnwnwu" - "wuwuwswtwuwrwqwrzqwqwszmyowpwrwpwuwqwuwuwuwpwmwmwnwbwPzqwswqwmwswq{pwnwswqwswowmwoxlwqwswswswswqwswqwswqwswqwlx" - "nwnwswqwtwqwuwuwuwqxowtwmwnwmwmwoytwiwtwtwswswpwtxqzpwswpxowswpwuwowuwpwswrwtwtwswtwtwrwtwqwtwtwrwswrwswnwowswq" - "wswowownwqwswtwtwswrwqwuwuwrwuyuwt~pwq~pwq~pwcwowuwozpwswowewswiwuwrwiwtwjwjwuytw\\wRwswoxuwHwtwpwswqwtxqwswqxo" - "wswqwswqwswqwswqwswqwswrwtwpwlwmwmwmwjwmwnwmwowrwswtwuwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwowowswqwtwoz" - "pzpzpzpzpzr~swm{q{q{q{nwqwjwmwowswqwswqwswqwswqwswqwswqwswr}rwuwuwqwswqwswqwswqwswqwtwpwswqwtw\\wuwuwqwswqwswqw" - "swqwswJ}qxf}t~rzp{rwrwrwrwqwtwpwtwrw qwawg}owuwpwuwtwuwswuwfwlwmwmwPwnwswowmwkwr|mwqwswowowswmw^yo}oyqwqwszq{rw" - "rwrwmwrwqwmwnwqwswrwowlwqwtwownwtwtwswtwuwrwqwrwnwqwswtwkwowpwrwpwuwqwuwuwuwqwuwnwnwmwbwQwswqwswqwmwswqwlwnwswq" - "wswowmwowuwmwqwswswswswqwswqwswqwswqwjwownwswqwtwqwuwuwuwqxowtwnwmwmwmwpwtyhwtwtwswswpwswqwtwpwswqwmwswpwuwpwtw" - "pwswrwtwtwswtwtwrwtwqwtwtwrwswrwswnwowswqwswpwnwnwqwsxuwuxswrwpyqwqwswjwkwqwuwuwrwrwqwuwuwewowuwnwnwswq{ownxuwi" - "wtxtwrzpwtwkwjwuwtwuw\\wRwswnwuwSzpwtwowtxqwrwrwtxrxn{q{q{q{q{q{s{pwlwmwmwmwjwmwnwmwowrwswtwuwrwqwswqwswqwswqws" - "wqwrwtwqwuwswswrwrwrwrwrwrwrwowozpwswqwswqwswqwswqwswqwswqwswswswowmwmwmwmwjwqwjwmwowswqwswqwswqwswqwswqwswqwsw" - "gwuwuwqwswqwswqwswqwswqwtwpwswqwtw[yoyoyoyoyGwmwdwuwuwpxnxnyqwrwqwtwpwtwoxpw rwswSwuwmwuwpwuwtwuxswewlwcwPwnxux" - "ownwnwswnwlwqwswowowswnwZygygwkwswrwrwqwswrwswpwmwmwrwswrwowlwqwswpwnwqwswsxqwswqwmwswrwswqwrwowpxtxowowswqwswo" - "wowlwbwQwswqwswqwmwswqwswpwnwswqwswowmwowtwnwqwswswswswqwswqwswqwswqwmwswpwnwswpxowswqwtwoxnwlwmwmw[xuxrwtxpwsw" - "qwtwpwswqwmwswpypwtwpwswrwtwtwsxuwuxrwtwqwtwtwrwswrwswnwnwuwpwswqwmwmwswq{rwrwowowswqwkwlwoypwtwoydwowuwnwn{owm" - "wlwgwrwfwtw^wrw6wswnwuwJwtwowtzswrwrwtzswmwswqwswqwswqwswqwswqwswswswowswqwmwmwmwjwmwnwmwowswrwsxqwswqwswqwswqw" - "swqwswrwrwqwswrxtxrxtxrxtxrxtxowowmwswqwswqwswqwswqwswqwswqwswswtxowmwswqwswqwswqwswnwqwjwmwowswqwswqwswqwswqws" - "wqwswqwswowoxtwqwswqwswqwswqwswpxowswpx Wwlwbwnxcwpwrwqzpwtwoxo|!ydwfwtwozpwsxszuxgxnxcwmwcwoxmyp{q{pymwpzoyowm" - "ypymwmwjwiwkwowrwrwqws{oyqzo{qwlzrwrwowlwqwrwq{rwqwswsxpypwlyqwrwqznwoznwowswrxsxpwp}qwbwPzqzoyozpyowmzqwswowmw" - "owswowqwswswswswpypzozqwlynxozpxowswrwrwpwn{owmwmwQxuxqzoxnyoypwswowpwrwqzpxuxq{qwtxq{qzpylwoyq}r{qwnyuypwpwrwo" - "wnydwcwcwcwnzq{rwqwpwmwkwgzHz]}U|owuw@wqwswrytwqwqyqwqwswqwswqwswqwswqwswqwuwr{ryp{q{q{q{nwmwnwmwozqwsxpyoyoyoy" - "oygwuypzpzpzpznwowmwuypzpzpzpzpzpzryuzryoyoyoyoymwqwjwmwnypwswpyoyoyoyoyfzozpzpzpzpwnzow \\wOwnwXw[w SwGz kx" - "0x lxdx gw[w=wiw*wbyowoyGwKwowewawcwow YwOwoz Ewjwuwdw 7w 9w Iwnwlw \\w 0|*y[x=wiw,xWw=wKwowewawcwo" - "w Yw hwVx 8w 9w Jxmwnxp" }; - - // Define a 26x32 font (normal sans). - static const char *const data_font_normal[] = { - " #{}~}a{|y~f{|y~}f{|}|x{}|j{|y}y{|y}g{}y~}|2y~|a{}~}f{}y~|" - "gy}|yy}|i{}~}a{}~}f{}y~}gy}|yx}N{|}|x{}|hy~|ay~|fx~|g{}y~|y{}~j{|y~|yy~}5{}~}a{}~}f{}y~}gy~}yy~}e{|y~ " - " 2{}~}c{|y~f{|y~}~}h{}w~y}~|j{}y~y{}y~h{}~y}y~|2y~|c{}~}f{" - "}~y}~|hy~}yy~}hy~|c{|~}f{|~y}~|hy~}y{}y~O{}w~y}~|gy~|cy~|fy~|}~|i{|~y}y~}~}j{|y~|yy~}4{}~|c{}~}f{}~|}~}hy~}yy~}" - "ey~| g{|}y~} J{}~|dy~|fy~y{}~|i{~}{|}y~}i{}y~y{}y" - "~i{|~}x{~}2{|y~d{|~}f{|~}yy~hy~}yy~}gy~cy~f{|~}y{}~|iy~}y{}y~P{|~}{|}y~|ey~d{}~|fy~x{}~|j{}~y{|}~}i{|y}|yy}|3{}" - "~|e{}~}f{}~|y{|~|iy}|yx}f{}~| fy~y}~} k{|y~| /{|y~| y{}" - "~} Xy|e{|}|f{|}wy|5{|~|x{}~1{|}|ey|ey|wy|M{|}|e{|}|fy|wy| g{|}|3{|y~|_{}~}g{|y~2{}~|y{}~|5{|y~^y~}g{}y~N{|" - "}|^{|}|g{|}| s{}~}_{|y~|gy~} Z{}~}_{|y~|gy~} )y}| -{|y~ Jy}|yy}| " - "X{}y~ 4{|~}y{|~} P{| n{|y~`{|y~fx~}3{}~x{|~|4{}~}`{}~}g{|x~}N{}~}`{|y~|gx~| sy~|`y~|g{}x~| Z{}~}`y~}g{}" - "x~|I{}y~ 1{|x~|oi| r{|~|O{|d{|y}|j{|y}|u{|y}|h{| \"{|}x~}|Ny~}g{|y~y{|~}g{|~}x{|~}i{|~l{|}y~}|s{|~}l{|}x~}|e" - "{|y~by~g{}~}b{~} S{|y~i{|}x~}|i{|y}x~|i{|y}x~y}i{|y}w~}|d{}x~kq~|i{|}w~}|m{}o~k{|}x~y}h{|}x~}| B{|}w~}L{|x~j{|s" - "~y}g{|}w~}|o{|s~y}|k{|o~n{|p~j{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|t{|x~n{|y~|i{|x~}r{|x~}s{|x~|sy~}l{|}x~y}|l{|" - "s~}|i{|}x~y}|m{|s~}|hy}w~y}|ok~}r{}y~r{|y~|r{}y~|p{}y~vy~}t{|x~sy~}ux~rx~q{}y~|r{}y~|r{|l~l{}v~hy~|c{|v~|f{|}|L" - "{}~}M{}y~@{}~}O{|}w~R{}y~`{|y~d{|y~h{}y~`{|y~ ay}y~}h{}~}h{}y~y} Wy}x~}|O{|y}w~}| xx~} I{|}x~}f{|x~i{|o~m{|" - "o~m{|y}x~}|f{}y~k{|m~}r{|y~|w{}y~vy~}n{|}x~y}|My}Iy}|J{}~| q{|}x~y}T{}y~r{}~}R{}w~}|j{|y~}yy~}O{|}w~} \\{|t~}h{" - "|}y~}M{|}x~}|h{|}x~}|e{|y~L{|}t~|7y}y~}f{|}x~}Uy|y}|py}p{|n{|t{|}w~}r{|y~P{|x~e{|x~e{|x~e{|x~e{|x~f{}v~|jk~|o{|" - "}w~}|m{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|x~|sy~}l{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}" - "|O{|}x~y}|y{|~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~|r{}y~|p{|y~|b{|}x~}|h{}~}b{|y~|g{}~|}~|h{|y~}|" - "{}~iy~}yy~}i{}~|y{}~|3{}~}b{|~}fy~{}~|i{|y~|{|y~}iy~|ay~}g{}~}y~gy~}yy~}i{|y~}wy|j{|y~}y{}~hy~|b{}~}g{|~}|~}h{|" - "}y~}{|~}j{}y~y{}y~|4y~|b{}~}g{|~}{y~h{}y~y{|y~|f{|y~|k{}y~by~}y{}y~ ev~o{}k~} r{}~O{|~e{}v~l{}v~w{}w~}j{}~ Y{}" - "o~ S{|s~}Oy~}g{|y~y{|~}g{}~|x{}~|i{|~m{|y~y}y~|ty~l{}t~}f{|y~c{}~}fy~b{~} S{}~}j{}t~}kt~|j{}r~|l{|r~}f{|w~kq~|" - "j{}s~|n{}p~}m{|r~|l{|s~| D{}s~|i{|y}y~y}|hw~|k{|p~|k{|q~}q{|p~}|m{|o~n{|p~l{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|u{|" - "x~m{|y~|i{|w~|sw~}s{|w~sy~}n{}r~}m{|q~}l{}r~}n{|q~}k{|q~|pk~}r{}y~r{|y~|r{|y~}py~}v{}y~t{}x~|u{|y~|u{|y~}t{}y~|" - "py~}s{|y~}q{|l~l{}w~}h{}~}c{|v~|gw~}L{}~|N{}y~@{}~}P{|u~R{}y~`{|y~d{|y~h{}y~`{|y~ e{}y~ {}w~}h{}~}h{}v~ Ys~}Q" - "{|r~| yv~ K{}t~|hw~|j{|o~m{|o~n{}r~|h{}y~k{|m~}r{|y~|w{}y~vy~}p{}r~}O{}x~Jy~|K{}x~|/{~|f{}t~}Ty~|t{|y~|Ss~j{|y" - "~}yy~}i{|}v~}|j{}~w}y~ v{|}v~}|k{|t~}i{|y~}x~N{}~}|}y~|i{|y}y|}~}fy~|N{|u~y}y~|8{|~y}~}g{|y~x}y~W{|w~}q{}~}s{}x" - "~}q{|y~t{|}x|}~}s{}~|Pw~|fw~|fw~|fw~|fw~|fw~|j{|k~|q{|q~}o{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|w" - "~sy~}n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}R{}r~}|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|py~}s{|y~}o{|y~|cs~}h{" - "}~}cy~|g{|~}yy~i{|y~}w~}iy~}yy~}hy~y}~}1y~|d{|y~f{}~|{|~}i{|y~|{|y~}i{|y~b{}~}g{|~}{|~}hy~}yy~}h{|y~y}y~}|k{|y~" - "}y~}~}h{|y~c{|~}fy~y{|~|i{}~}v~|j{}y~y{}y~|4{|y~c{|~}fy~y{|~}i{}y~y{|y~|fy~|j{}y~by~}y{}y~ f{|y~{}~|p{|k~| r{~" - "}Oy~}g{}u~}n{}t~y{}u~}l{}y~} \\{}m~ T{|x~}|{y|y~|Py~}g{|y~y{|~}gy~|xy~|i{|~my~|y{|y~u{}~}m{}y~}|y{|y}f{|y~d{|y" - "~e{}~}hy|x{~}x{| Wy~|k{|y~}|{|}y~}lx~y}y~|jx~}x|}x~|m{|~}v|x~}gv~ky~s|j{}x~w|}~|nr|}y~|mx~}|{y|x~|mx~y|{|}y~| E" - "y~}x|}x~k{}q~|k{|w~}k{|y~u|}x~l{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~r|m{}x~}v|}x~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|v{|" - "x~l{|y~|i{|w~}t{|w~}s{|w~}ty~}o{}x~}|{y|}x~n{|y~v|}x~|n{}x~}|{y|}x~o{|y~v|}x~}m{|x~}v|}~|pt|y~}u|q{}y~r{|y~|qx~" - "q{|y~|v{|y~|u{}x~|u{}y~|t{}y~|v{|y~}o{|y~|tx~op|}y~}l{}~}e{|y~`{|y~|h{}v~}L{}~|O{}y~@{}~}Py~}|O{}y~`{|y~d{|y~h{" - "}y~`{|y~ e{}y~ !{|y~}e{}~}e{|y~| [{}y~|x{}y~|jy}~y}|ix~|w{|}| w{}y~| M{}y~|y{|}y~i{|w~}ix~r|m{|y~q|p{|w~}x|}x" - "~}l{|y}x~y}|n{|y~q|y~}r{|y~|w{}y~vy~}q{}x~}|{y|}x~Q{}v~Ky~|L{}v~|0{~|g{|y~}|y{|y}T{}y~t{}~}i{}~}h{}y~|x{|}P{}~y" - "}x|y}~}k{|v{}~| x{}~y}x|y}~}Qy~x{|~}J{|y~cy~g{}~|Mt~y{}~|5{|~}gy~|x{}~}U{|~}r{|y~r{}y|~}qy~|ny~t{|~}P{|w~}g{|w~" - "}g{|w~}g{|w~}g{|w~}fw~}j{}y~y|y~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}" - "p{|w~}ty~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~T{}x~}|{y|v~|r{}y~r{|y~|q{}y~r{|y~|q" - "{}y~r{|y~|q{}y~r{|y~|p{|y~|tx~n{|y~|d{}y~|x{}y~|h{}~|e{}~|f{~}x{|~}j{|}xx}hy}|yy}|h{|}y~}/y~dy~|g{|~}x{|~|j{|y}" - "|yy}|h{|~}d{|~}f{}~x{}~|iy}|yy}|iy|w~|h{}~y{|}~}f{|~}e{|~}f{}~|x{}~|j{}|y{|y}|i{|y}y{|y}2{|~}dy~f{}~|x{}~|j{|y}" - "y{|y}|g{}~|i{}y~by}|y{|y}5{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|f{}~}{y|ny~}q{}y~ r{|~O{}x~|hs~|p{|s~y|s~|n{|w" - "~} ^{}y~}| Ix~|u{}|Py~}g{|y~y{|~}gy~wy~k{|}v~y}|s{|y~w{}~|w{|y~ly~}_{|y~d{}~}dy~|iy~}y{~}{|y~|hy}| o{|y~jx~v{}" - "y~l{|x{|y~|j{}|u{|x~d{}y~|i{}~|}y~ky~|d{|y~}]{}y~m{|y~|v{|y~}n{}y~|v{}y~ E{}u{}y~|n{|x~}|w{|}y~}|m{}y~}y~k{|y~|" - "u{|y~}n{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}r{}~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~}y~t{}~}y~}s{|" - "v~ty~}p{}y~}t{}y~}o{|y~|v{|x~o{}y~}t{}y~}p{|y~|v{|x~mx~r{|iy~}k{}y~r{|y~|q{|y~|r{}y~u{|y~|uy~}~}u{}y~rx~vx~m{}y" - "~u{}y~|e{|y~}k{}~}dy~|a{|y~|i{}y~|{}y~| y{}y~@{}~}Py~|N{}y~0{}y~`{|y~ e{}y~ !{|y~d{}~}dy~} [y~}v{}~}ju~}jy~| n" - "{}y~ N{|y~|v{}~}j{}y~}y~i{|y~}e{|y~|gx~}t{}y~}o{|}q~|p{|y~|ry~}r{|y~|w{}y~vy~}r{}y~}t{}y~}S{}t~Ly~|M{}t~|1{~|g" - "{}y~Ly~|v{|y~|i{}~}hy~}L{|y~|t{|y~|g{|~} {{|y~|t{|y~|T{|~|wy~f{}~|ay~ey|y~7{}t~y{}~|5{|~}h{|~}vy~U{|~}r{}~|p{|~" - "}r{|~}my~ty~O{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{|y~}y~jy~}yy~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{" - "|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|v~ty~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}V{}y~}t{}x~q{}" - "y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|o{}y~u{}y~|n{|y~|e{|y~|v{}~} A{|}|ey|e{}|wy|Py~}y|y~} ?{}~}h{}y~ p" - "{}r~|l{}r~|l{}r~|l{}r~|l{}r~|h{}~}k{}y~qy~}1{|~}dy}P{}v~|is~|p{|r~}s~}nu~|h{|w}|k{|y}sy}|jx}|j{|y}t{|y}o{}y~| " - "H{|y~|Gy~}g{|y~y{|~}h{|~}x{|~}l{}r~}s{|~}w{}~}w{}~|ly~}_{|y~dy~|d{}~}h{|y~|~y}~}|g{}~| o{}~}k{|y~|uy~}i{|y~|a{}" - "y~|e{|y~}j{|~}{}y~ky~|dy~}]{|y~}m{}y~tx~ny~}u{|y~|,{|X{|X{|y~|o{}y~|q{}y~my~}|y~|l{|y~|ty~}o{|x~p{|r{|y~|s{|x~o" - "{|y~|e{|y~|g{|x~p{|q{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|y~|uy~|y~}s{|y~|y~}uy~}q{|x~r{}y~|p{|y~|u{}y~|" - "q{|x~r{}y~|q{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|py~}s{|y~}ty~}v{|y~|y~uy~}r{|y~}x{}y~|ly~}w{|y~}e{|x~j{}~}d{}~}a{|y~|" - "j{}y~|x{}y~| {{}y~@{}~}Py~|N{}y~0{}y~`{|y~ e{}y~ !{}y~d{}~}d{}~} \\{|y~u{}y~j{}x|}y~|kx~| o{|y~| O{}~}u{|y~jy" - "~}|y~|i{|y~}f{|y~|h{}y~|rx~|q{|w~y}y~y}x~}q{|y~|ry~}r{|y~|w{}y~vy~}s{|x~r{}y~|U{}y~|y~}x~My~|N{}y~|y~|x~|2{~|gy" - "~}g{|p{}m{}y~v{}~}h{}~}h{}~}L{~}xy|}y|x{}~l{|}u~ {{~}p{}~T{|~|wy~f{}~|b{}~}g{}w~|7{}t~y{}~|5{|~}h{}~|v{}~|V{|~}" - "s{|~}o{|~}ry~n{|}~|u{}~}Oy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|l{}y~|yy~}j{|x~p{|p{|y~|e{|y~|e{|y~|e{|" - "y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|y~}uy~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|q{|}q{|" - "}p{|x~s{}x~|r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|ny~}w{|y~}m{|s~}|l{|y~u{|y~ 8{|w{|y~} _{} G{}y~ r{|" - "x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|i{}~|jy~|s{|y~|1y~}d{~|Q{|t~is~|p{}i~}os~j{|s~|m{" - "|y~sy~|jw~j{|y~|u{}y~p{|y~| Gx~Fy~}g{|y~y{|~}h{}~}x{}~}m{|y~}{|~x{|}s{|~}w{}~}x{|~}ky~}_{|y~e{|y~c{|y~f{}x~}|e" - "{}~| oy~|k{}y~t{}y~i{|y~|a{|y~|dy~|jy~|{}y~ky~|e{|y~|]{}y~|m{}y~ty~}o{|y~|ty~}/{|}~}Xy~}|[{|y~|p{}y~|o{}y~o{|y~" - "|{y~}l{|y~|ty~}o{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|e{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{|y~|i{|y~|}~}v{|y~{y~}s{" - "|y~|}y~uy~}q{}y~|qx~p{|y~|u{}y~|q{}y~|qx~q{|y~|u{}y~|o{|y~|_y~}k{}y~r{|y~|p{}y~s{}y~|t{}y~v{|~}{y~|w{|y~|q{}y~|" - "{|y~}k{|y~|xx~dx~|j{}~}d{|y~a{|y~|k{}y~|v{}y~|9{|y}x~y}j{}y~y{}x~}|h{|}x~y}|j{}x~}|{}~}k{|}x~}|j{|s~|i{}x~}|{}~" - "}n{}y~y{}x~}|h{|y~d{|y~h{}y~u{|y~}j{|y~m{}y~y{}x~}w{|}y~}|p{}y~y{}x~}|i{|}x~}|k{}y~y{}x~}|i{}x~}|{}~}k{}y~y{}x~" - "k{|}w~y}|k{|r~l{}~}t{}~}oy~}s{}y~r{}~}v{}y~}v{}~}r{|y~|u{|y~|oy~}s{}y~n{}p~h{}y~d{}~}d{}~} t{}x~}|y{|~}n{|y~u{}" - "~}e{}y~k{|w~y}|g{|}w~y}l{}y~y{}x~}|n{}~}|s{}y~iy~}i{}~}t{}~}p{|y~|r{}y~n{|y}y{}y~}lm~p{}y~x{|y~x{|y~|k{}w~}|j{|" - "}q~|q{}n~ny~|ty~|l{|y~|{y~}h{|y~}g{|y~|hy~}q{|y~}qx~}y{}y~y{|x~|r{|y~|ry~}r{|y~|w{}y~vy~}s{}y~|qx~V{|y~|{y~y|y~" - "}Ny~|O{|y~|{y~|{y~}Ny~}e{|}w~}|jy~}h{|y~r{}~}my~|x{|y~|h{}~}h{|y~}Ny}x{}u~|yy}n{}y~w}y~ y}y{}v~}|xy}T{~}x{|~}f{" - "}~|c{|~}fx|y}|Q{}~}t{}~}ns~y{}~|5{|~}h{}~|v{}~|V{|~}sy~n{|~}s{}~|p{}x~}u{|y~f{|y~|h{|y~|{y~}i{|y~|{y~}i{|y~|{y~" - "}i{|y~|{y~}i{|y~|{y~}i{|y~|{y~}ly~}xy~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|r{|y~}r{|y~|" - "}y~uy~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~qy~}s{|y~}q{}y~|t{}~}x~r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}" - "y~r{|y~|n{|y~|xx~l{|q~}m{}y~w{|w~l{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}n{|y}x~y}w{|}x~}|l{|}x~y" - "}|j{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|g{|y~d{|y~d{|y~d{|y~e{|}v~|l{}y~y{}x~}|i{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~" - "}|g{|x~f{|}x~}|{}~|o{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}oy~}s{}y~n{}y~y{}x~}|my~}s{}y~;y~}xy~|y{|y~|py~}s{|y" - "~|py~}s{|y~|py~}s{|y~|py~}s{|y~|j{}~|j{}y~sy~}1y~}d{|~Q{|s~}j{}t~o{|i~}p{}s~}kx~}y|}x~m{|y~sy~|k{|w~|jy~}uy~}py" - "~} Fy~}Fy~}g{|y~y{|~}m{|k~}q{}y~y{|~n{|~}w{}~|xy~j{}y~|`{|y~e{}~}by~|g{|x~}d{}~| p{|y~jy~}t{}y~i{|y~|a{|y~|e{|" - "y~|k{}~}y{}y~ky~|{|g{}y~\\x~l{|y~|v{|y~|o{|y~|tx~i{}y~d{}y~a{|}w~}Xv~}|^x~p{|y~l{}~}p{}y~y{|y~|m{|y~|ty~}ox~e{|" - "y~|qy~}p{|y~|e{|y~|gx~d{|y~|ry~}k{|y~|e{|y~|j{|y~|{}y~}h{|y~|i{|y~y|y~v{}~}{y~}s{|y~|{y~}vy~}qx~p{}y~|q{|y~|u{|" - "y~|qx~p{}y~|r{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|p{|y~|ty~}s{}y~|w{}~|{}~|w{|y~|py~}{x~i{}y~y{}y~|e{}y~|i{}~}cy~|b{|y" - "~|l{}y~|t{}y~|;{|r~|l{}y~|t~|j{}s~|m{|t~|}~}l{}s~|l{|s~|k{|t~|}~}n{}y~|t~|i{|y~d{|y~h{}y~v{}y~}i{|y~m{}y~|t~y{}" - "u~}q{}y~|t~|l{|s~}l{}y~|t~|l{|t~|}~}k{}y~|v~l{|r~k{|s~}l{}~}t{}~}o{}y~sy~}r{|y~v{}x~vy~}q{}y~|w{|y~}n{}y~sy~}n{" - "|p~h{}y~d{}~}d{}~} v{|t~|{}~|n{|y~u{}~}e{|y~|k{|t~|j{}s~|m{}y~|t~|o{}x~|ty~}ix~i{}~}t{}~}py~}q{|y~|p{|y~}{}v~|n" - "m~p{}y~x{|y~x{|y~|ls~|l{|o~|q{}n~o{|y~|t{}~}l{}y~y{}y~|h{}y~}h{|y~|i{|y~|p{}y~r{}y~|x{}y~x{|x~r{|y~|ry~}r{|y~|w" - "{}y~vy~}sx~p{}y~|p{}b{}|yy~|{|}b{}|hy~|i{|}s{}|ly|yy~|y{}My~}g{|r~k{|y~|gx~|}x~}|}y~|m{}y~xy~}g{}~}g{}x~|Q{|~|y" - "y~}|yx|y{|~|p{|~}w{|y~gy|w{|<{|~|y{}~}x|y~|y{|~|U{}y~y}y~e{}~|d{|y~a{}~Q{}~}t{}~}n{}t~y{}~|5{|~}h{}~|v{}~|m{|v{" - "|k{|~}t{}~|n{|~}t{|~}ox|}y~v{}~|f{|y~|h{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{}y~m{|y~|xy" - "~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|{y~}vy~}qx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~" - "|sx~p{}y~|r{|y~}u{|x~px~t{}~}{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|m{}y~y{}y~|l{|y~v|}x~}n{}y~x{}y~|" - "k{|r~|l{|r~|l{|r~|l{|r~|l{|r~|l{|r~|q{|r~|{}s~n{}s~|l{}s~|k{}s~|k{}s~|k{}s~|i{|y~d{|y~d{|y~d{|y~g{|r~l{}y~|t~|l" - "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}h{|x~h{|q~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}o{}y~sy~}n{}y~|t~|n{}y~sy~}<{}~" - "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}t{|~y|y~q{}~}q{|y~j{}~|j{|y~|u{|y~|<q|}y~w|p{|y}uy}Qq~}k{|u~}o{|i~|q{|" - "q~}m{}y~uy~}n{|y~sy~|k{}w~|j{}y~v{|y~|q{|y~ H{}o~My~}fy|xy|m{|k~}q{}~}y{|~n{|y~wy~|y{}~|ix~_y|ey~|by~}i{|}~}~}" - "y~}f{}~| p{|~}jy~}t{|y~|j{|y~|a{}y~f{|}y~}k{|y~x{}y~kt~}|ky~}{|w~}|e{|y~|kx~|x{|y~}n{|y~|tx~i{}y~d{}y~d{|}v~}|q" - "k|p{|}w~}|a{}y~|p{}~|w{|}y~}|{y|xy~|qy~}xy~}m{|y~|u{|y~|p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}d{|y~|ry~}k{|y~|e{|y~|" - "j{|y~|}y~}g{|y~|i{|y~|{y~|wy~|{y~}s{|y~|{}y~vy~}r{|y~}p{|y~|q{|y~|u{}y~|r{|y~}p{|y~|r{|y~|u{}y~mx~}`y~}k{}y~r{|" - "y~|oy~}u{|y~|s{|y~|wy~|{|~}w{}y~o{|v~|hy~}|y~}e{}y~}h{}~}c{}~}b{|y~|m{}y~|r{|y~|<{|}y|x{|}y~l{}w~|y{|x~|lx~}|yy" - "|}|mx~|y{|}x~}mx~y|y{|x~iy~|h{|x~|y{|}x~}n{}w~|y{|x~i{|y~d{|y~h{}y~w{}y~|h{|y~m{}w~|y{|y~}|~}|{|}y~|r{}w~|y{|x~" - "lx~|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}w~w|ly~}|xy|}i{}y~g{}~}t{}~}o{|y~|u{|y~|r{|y~|ww~vy~|px~wx~m{|y~|u{|y~|f{|" - "y~}h{}y~d{}~}d{}~}6{|}x~|x{}x~|o{|y~}|{|}y~{y~m{|y~v{|y~|dy~|l{}~}x{|x~|l{}y~}|yy|}|m{}w~|y{|x~n{|}~}u{|y~|j{|x" - "~|j{}~}t{}~}q{|y~|py~}q{|x~y|y~y|x~ny|y~}w|}y~}|p{}y~x{|y~x{|y~|mx~|y{|x~|n{|x~|x{}x~y|pu|y~}v|o{|y~s{}y~ly~}xy" - "~}g{}y~|i{|y~|i{}y~o{}y~|sx~w{}y~w{}y~|s{|y~|ry~}r{|y~|w{}y~w{|y~}t{|y~}p{|y~|qy~}_y~|`{|y~|iy~|j{|y~}u{|y~|iy~" - "|Jy~}h{|x~y|~|{|}k{|y~|fp~|ky~}{|y~f{}~}h{}~y}y~}|Sy}y{}~}qy}p{|~}w{|y~h{|~|x{}~<y}x{}~}x{|~}xy}T{|}y~}d{}~|e{|" - "~}`{}~|R{}~}t{}~}n{}t~y{}~|5{|~}h{|~}vy~l{|~|xy}l{|~}u{|~}m{|~}ty~|k{}~|x{|~}e{|y~|hy~}xy~}jy~}xy~}jy~}xy~}jy~}" - "xy~}jy~}xy~}jy~}xy~|n{}y~wy~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~r{|y~|{}y~vy~}r{|" - "y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|q{|y~}w{|x~p{|y~}u{|~}y{|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y" - "~r{|y~|q{}y~r{|y~|ly~}|y~}k{|y~|ux~n{}y~y{|y~|j{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y" - "|x{|}y~q{|}y|x{|}v~|x{|y~}px~}|yy|}|mx~y|y{|x~lx~y|y{|x~lx~y|y{|x~lx~y|y{|x~i{|y~d{|y~d{|y~d{|y~gx~|x{|y~}m{}w~" - "|y{|x~lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}i{|x~hx~|y{|}y~}m{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~" - "}t{}~}o{|y~|u{|y~|n{}w~|y{|x~|o{|y~|u{|y~|={|y~vy~|w{}~|s{|y~o{}~|s{|y~{}y~}y{|y~}{}~|s{|y~t{|y~}{}~|s{|y~o{}~|" - "ky~|iy~}u{}y~;k~}qw~u{~|R{}p~|k{}w~}mi~q{|o~|ny~|u{}y~n{|y~sy~|ky~}y~}j{|y~|w{}y~p{}~} Hx}y~t}|My~}M{|y~x{|~}l" - "{}y~y{|~m{}~}y{}~}y{|~}i{|w~I{|y~|b{}y~j{}~}|{~}{|y~|h{}~| p{}~|jy~}t{|y~|j{|y~|b{|y~}j{}u~}jy~|x{}y~kr~}ly~y}t" - "~}f{}y~i{}t~|ly~}u{|x~|j{}y~d{}y~f{}v~}|nk~}n{|}w~}|e{}y~|p{|~}w{|u~y}~x{|~}r{|y~|x{}y~m{|y~|xy|}y~}o{|y~|e{|y~" - "|q{}y~p{|y~r|m{|y~s|o{|y~|d{|y~q|y~}k{|y~|e{|y~|j{|v~}f{|y~|i{|y~|{}~}x{|~}yy~}s{|y~|yy~}wy~}r{|y~|p{|y~}q{|y~|" - "ux~q{|y~|p{|y~}r{|y~|v{|y~}m{|v~}y|ey~}k{}y~r{|y~|o{}y~u{}y~qy~}x{|y~y{|y~wy~}n{}x~}g{|v~e{|y~}g{}~}c{|y~b{|y~|" - " o{}~}m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|uy~}jy~|h{}y~u{}y~}n{}x~v{|y~|j{|y~d{|y~h{}y~x{}y~|g{|y~m{}x~v{}x~|vy~}r{}" - "x~v{|y~|n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}x~h{|y~a{}y~g{}~}t{}~}ny~}u{}y~py~|x{|y~}~|x{|y~o{|y~}y{}y~|l{}~}u{}~" - "}ex~g{}y~d{}~}d{}~}6y~y}y~|{}y~}y~}p{}y~vy~}y~m{|y~x{|}x~c{}~}m{|y~u{}y~l{}~}e{}x~v{|y~|n{|y~u{}~}i{}x~}j{}~}t{" - "}~}q{}y~o{}y~q{}y~|{|y~y{|y~}my~|w{|y~|o{}y~x{|y~x{|y~|n{}y~ux~n{}y~u{}y~|iy~|j{|n~m{|y~|x{}y~f{}y~|j{|y~|i{}y~" - "o{|y~|t{|y~}w{}y~w{|y~}s{|y~|ry~}qy~}w{}y~w{|y~|t{|y~|{r~y|y~}rx~|_y~|_{}y~|jy~|k{|x~s{}y~|jy~|Jx|h{}y~|y{~|h{|" - "y~|f{|y~}|y{}y~|n{|u~{u~|j{}~}i{|~}y{|}y~}T{~|yy~p{|~p{|~}wx~i{|y~|y{}y~ok|X{~|x{}~}x{|~}x{|~?k~}m{}~}_y~|R{}~}" - "t{}~}mt~y{}~|ix|J{|~}gy~|x{}~}l{|y~|y{}~}m{|~}uy~|u{|t{|~}u{}~}j{}~|xy~cy|h{|y~|x{}y~k{|y~|x{}y~k{|y~|x{}y~k{|y" - "~|x{}y~k{|y~|x{}y~k{|y~|x{}y~ny~}wy~}r|t{|y~|c{|y~r|m{|y~r|m{|y~r|m{|y~r|i{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{" - "|y~|yy~}wy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}p{|y~}y{|x~o{|y~|v{|y~wy~}s{}y~r{|y~|q{" - "}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|l{|v~j{|y~|u{}y~|o{}y~y{|y~`{}~}d{}~}d{}~}d{}~}d{}~}d{}~}i{}x~u{|y~|r{}y~|f{}y~|" - "uy~}n{}y~|uy~}n{}y~|uy~}n{}y~|uy~}j{|y~d{|y~d{|y~d{|y~h{}y~|v{|y~|n{}x~v{|y~|n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y" - "~|n{}y~|v{}y~|n{}y~|v{}y~|ix|i{}y~|w{|x~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}u{}~}m{}x~ux~n{}~}u{}~}<{" - "|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}u{|y~}y{|~}s{|~}o{|~}ky~|i{}y~uy~};k~}q{|{y~|w{}~R{|n~o{}o~}|r{|" - "k~}qm~|oy~|u{|y~n{|y~sy~|l{|y~|}y~iy~}wy~}p{}~} F{|y~|Fy~}M{}~}x{}~}l{|y~}y|~lu~xy~|xx}|q{|y~|x~ty|S{|y~a{}y~j" - "{}|x{~}x{}|h{}~| py~j{|y~|t{|y~|j{|y~|bx~i{}v~}|k{}~}w{}y~k{}y|x{|x~}mw~}|y{|x~|gy~}h{}u~}l{}y~|v{}x~|jx|dx|i{|" - "}w~}|kk~}l{|}v~}|i{}y~|o{}~|wy~}x{}x~wy~rx~w{|y~|n{|q~|n{|y~|e{|y~|q{}y~|q{|p~}n{|q~o{|y~|tu|q{|m~}k{|y~|e{|y~|" - "j{|v~e{|y~|i{|y~|yy~xy~|yy~}s{|y~|y{}y~|xy~}r{|y~|oy~}q{|y~|w{|x~}q{|y~|oy~}r{|y~v|}y~}k{|}t~}gy~}k{}y~r{|y~|o{" - "|y~}vy~}q{}y~x{|~}xy~|y{|y~|n{|x~e{}x~|ex~f{}~}by~|c{|y~| o{|y~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~|t{}y~jy~|hy~}u{|y~" - "}n{}y~|u{}~}j{|y~d{|y~h{}y~y{}y~|f{|y~m{}y~|v{|x~u{}y~r{}y~|u{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~|h{|y~a{}y~" - "g{}~}t{}~}n{}y~uy~}p{}~}x{}~}|~}x{}~}n{|y~y|y~}k{|y~uy~|f{}y~f{}~}d{}~}d{}y~7{}~x{|y~|~}x{}~py~}v{}x~}m{|y~{|v~" - "|i{}y~}|{}~}my~|ty~}m{}~}e{}y~|u{}~}my~|vy~|j{|y~}y~j{}~}t{}~}qx~o{|y~|ry~}y{|y~x{}y~my~|w{|y~|o{}y~x{|y~x{|y~|" - "ny~|u{|y~|oy~}ty~}iy~|j{|n~mx~w{|y~|g{|y~}j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}qx~w{}y~w{}y~|t{|y~|{r~|{y~" - "}sx~|^y~|^{}y~|ky~|l{|x~q{}y~|ky~|5{|y~|x{~|h{|y~|f{}~}v{|~}mw}v~w}|j{}~}i{}~|w{|y~}U{~y{|y~p{|~ox~y}~}y~j{}y~|" - "y{}y~nk~}Y{~w{}~}y{|}~|x{|~?k~}n{}y~v|ix}|}y~}Q{}~}t{}~}m{|u~y{}~|iy~}Ly|}~}y|i{|y~x}y~j{}y~|y{}y~n{|~}v{|~}v{|" - "y~}u{|~}v{|y~y{}w~}wx|{|}y~x{}~|uy~}Wx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|l{}y~w{|y~|p{}y~|wo~t{|y~|c{|p" - "~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|mq~u{}y~|s{|y~|y{}y~|xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{" - "|y~|oy~}o{|y~}|x~n{|y~|vy~|wy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|k{}x~|j{|y~|u{|y~|o{}y~y{|y~|a{|y~d{" - "|y~d{|y~d{|y~d{|y~d{|y~i{|y~|t{}~}ry~}ey~|t{}y~ny~|t{}y~ny~|t{}y~ny~|t{}y~j{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~|u{}" - "~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}Ty~}w{|~}y~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{|y~uy~|m{}y~|u{" - "|y~|o{|y~uy~|<{}~u|y~}w|{y~s{}~n|{y~s{}~|x{}w~}wy~s{}~|v{|y~}wy~s{}~|vy~}vy~ky~|i{|y~|w{|y~|4{|y~}i{}~}w{~}Rm~}" - "qk~|r{}l~q{|m~|p{|y~sy~|o{|y~sy~|l{}y~{|y~|j{}y~x{|y~|p{}i~ V{|y~|F{}~}M{}~|x{}~|k{}v~}|m{|y}|x{}~}y{|v~}s{|y~" - "|{|x~v{|y~S{}y~a{|y~e{~}jt|y~}u| w{|~}j{|y~|t{|y~|j{|y~|cx~|hw|}y~|m{|y~v{}y~cx~|nx~}ux~h{|y~|j{|y~}|{y|x~m{|x~" - "|y{|}w~|={}w~}|E{|}v~|l{|y~|ny~w{}~}v{}y~wy~s{|y~|vy~}n{|q~}|o{|y~|e{|y~|q{}y~p{|p~}n{|q~o{|y~|u{|u~}r{|m~}k{|y" - "~|e{|y~|j{|y~}x~f{|y~|i{|y~|y{}~|{|y~xy~}s{|y~|xy~}xy~}r{|y~|oy~}q{|q~}p{|y~|oy~}r{|r~}h{|y}u~|iy~}k{}y~r{|y~|n" - "x~w{|y~|q{}y~x{}~|x{}~|y{|y~|nw~|ey~}e{}y~|f{}~}b{}~}c{|y~| v{|y}t~m{}y~sy~|o{|y~|f{|y~|ty~}o{|y~|t{|y~jy~|i{|y" - "~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{x~|e{|y~m{}y~ty~}u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~|ty~}k{}y~g{|y~}b{}y~g" - "{}~}t{}~}n{|y~|w{|y~o{|y~x{}~y|y~xy~}m{}w~}iy~|w{}y~f{}y~|g{|y~|d{}~}d{|y~}jy}y~}|t{|}X{~}w{|y~}v{~|r{|y~|v{|x~" - "|m{|y~{|v~}|ku~|y~}n{|y~|t{}y~m{|y~}f{}y~t{}~}m{}y~w{}y~i{}y~{y~}k{}~}t{}~}qy~}ny~}s{|y~|y{|y~x{|y~my~|w{|y~|o{" - "}y~x{|y~x{|y~|o{|y~sy~|p{|y~|t{}y~iy~|j{|y~s|}y~n{|y~}vy~}gx~|j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}q{}y~|x" - "{}y~x{|y~}s{|y~|{r|yy~}tx~}l|ly~|mk|x~|ly~|m{|x~o|x~|ly~|5{}y~w{~|j{}r~}ky~|uy~i{|x~}e{}~}i{}~}v{|y~V{|~y{|y~o{" - "~|o{}x~|{y}k{}y~|y{}~}mk~}Z{|~w{}u~|v{~|@t|y~}t|n{}t~i{|}x~y}P{}~}t{}~}k{|}x~y{}~|iy~}Lt~|i{|}x~}h{|y~|y{}y~|rt" - "~|yy~ux~}wt~|y{}~|{|~}y|}y~x{}v~}|y{|y~u{}y~}m{|y}i{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}ly~|vy~}py~" - "}vo~t{|y~|c{|p~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|m{}s~}u{}y~|s{|y~|xy~}xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|o" - "y~}t{|y~|oy~}t{|y~|oy~}n{|v~m{|y~|wy~|vy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|u{}y~|o{}y~xx~|" - "i{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~p{|y}t~s{|y~s{|y~|f{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~j{|y~d{" - "|y~d{|y~d{|y~i{|y~|t{}~}n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~pk~}q{|y~|w{~}{}y~n{}~" - "}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}my~|w{}y~l{}y~sy~|ny~|w{}y~;{}~|}p~{y~s{}~|}p~{y~s{}~|w{}x~vy~s{}~|w{|y~}vy" - "~s{}~|vy~}vy~ky~|h{}~}w{}y~3y~}h{|y~x{|~|S{|l~r{}k~}qm~|p{}o~}o{|y~sy~|o{|y~sy~|ly~}yy~}j{|y~|y{|y~o{}i~ X{|y}" - "y~u}K{}~}My~|xy~i{|}u~}i{|y~y{|y~|{|y~|t{}~}x{|x~w{|y~S{}y~a{|y~|f{~}jk~} x{}~}iy~}t{|y~|j{|y~|d{}y~|b{}y~|ny~|" - "v{}y~c{|y~}nx~|u{}y~|ix~j{|y~|v{|y~}m{|t~y}y~|=x~}?{|x~}l{}y~m{~}wy~|v{|y~w{}~s{}y~u{}y~n{|y~|v{|}y~|p{|y~|e{|y" - "~|q{}y~p{|y~|e{|y~|h{|y~|u{|u~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|}x~g{|y~|i{|y~|y{|y~{}~}xy~}s{|y~|x{|y~|yy~}r{|y~|p{" - "|y~}q{|s~}|o{|y~|p{|y~}r{|q~|ey|w~iy~}k{}y~r{|y~|n{|y~|x{}y~p{|y~|yy~|x{}~}y{}y~n{}v~ey~}f{}y~|e{}~}b{|~}c{|y~|" - " w{}q~m{}y~sy~}o{|y~e{|y~s{}~}o{|n~jy~|i{|y~s{}~}n{}y~t{}~}j{|y~d{|y~h{}v~c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}" - "y~n{}y~sy~}p{|y~s{}~}k{}y~f{}w~}|f{}y~g{}~}t{}~}m{}~}w{}~}o{|y~|yy~yy~xy~|lw~h{}y~wy~}g{}y~|i{}w~}c{}~}c{|w~}o{" - "|s~}w|}~}X{~|vy~|vy}r{|y~tx~l{|y~w{|}x~|m{}y~x{}x~|n{|y~s{}y~l{|}v~j{}y~t{}~}m{|y~|xy~}j{|y~|{}y~k{}~}t{}~}qy~}" - "vy~|vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{|y~sy~}p{|y~s{}y~iy~|j{|y~s{}y~n{}y~u{}y~h{}y~|i{|y~|i{}y~|" - "p{}y~s{|y~}w{}y~w{|y~}s{|y~|ry~}px~x{}y~x{}y~|s{|y~|p{|y~}u{}h~ly~|m{}h~ly~|mg~ly~|J{}~}i{}y~w{~|j{}r~}ky~ty~|i" - "x~d{}~}i{}y~|vy~|W{|~y{|y~o{~|X{}y~xy~}^{|~}Z{|~w{}~}|}~}u{~|9{}~| v{}~}t{}~}hy~y{}~|iy~} s{|y~}y{}y~|st|y{}~|v" - "{}~|~}wt|y{|~}sy~|wx|v{}~|vy}|~}m{|y~i{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~q{|y~|vy~}k{|y" - "~|c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{|y~|x{|y~|yy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|" - "y~}t{|y~|p{|y~}t{|y~|p{|y~}m{}x~|m{|y~|x{}~|v{|y~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|ux~n{}y" - "~x{|x~}k{}q~l{}q~l{}q~l{}q~l{}q~l{}q~q{}f~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y" - "~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~pk~}q{|y~wy~y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{}y~wy~" - "}l{}y~sy~}n{}y~wy~};{}~|}q~}{y~s{}~|}q~}{y~s{}~|x{|w~}wy~s{}~|x{|y~}uy~s{}~|vy~}vy~ky~g{|y~|xy~|4{}y~fy~|yy}R{|" - "l~ri~po~|n{}p~n{|y~sy~|o{|y~sy~|m{|y~|y{}y~iy~}y{}~}o{}~} H{}r~}K{}~}N{|y~x{|y~f{|~}w~j{}~|y{}~}x{|y~ty~|w{|x~" - "x{}~}S{}y~a{|y~|f{|ik~}T{}u~|Ly~|iy~}t{|y~|j{|y~|e{}y~|`y~}o{}~}u{}y~by~}nx~t{|y~|j{|y~}jy~}t{}y~k{}x~}|{}y~<v~" - "}|hk|g{|}w~}l{}~}n{|~}wy~ty~wy~sy~}t|y~|o{|y~|t{}y~p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}py~}r{|y~|ry~}k{|y~|e{|y~|j" - "{|y~|{}y~}h{|y~|i{|y~|xy~|y~|xy~}s{|y~|wy~}yy~}r{|y~}p{|y~|q{|y~w|k{|y~}p{|y~|r{|y~|w{}x~bx~|jy~}k{}y~r{|y~|my~" - "}xy~}oy~}{|y~w{|y~yy~}o{|y~}{y~}fy~}g{|y~}d{}~}ay~c{|y~| x{}y~}|w{|y~m{}y~sy~}o{|y~e{}y~s{}~}o{|n~jy~|i{}y~s{}~" - "}n{}y~t{}~}j{|y~d{|y~h{}w~}c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{}y~s{}y~n{}y~sy~}p{}y~s{}~}k{}y~e{|u~}h{}y~g{}~}t{}~}" - "m{|y~wy~|ny~}{|~}y{}~|{|y~k{}y~}h{|y~|y{|y~|h{|y~}h{}w~|c{}~}c{|}x~}oy~}x|}r~|X{~|v{}~|v{~}r{}y~ty~}l{|y~t{}y~n" - "{|y~|wx~|n{|y~s{}y~l{|u~j{}y~t{}~}ly~}y{|y~|j{}y~y{|y~|l{}~}t{}~}r{|y~}vy~|vy~}s{}y~x{|y~x{|y~|ny~|w{|y~|o{}y~x" - "{|y~x{|y~|o{}y~sy~}p{|y~s{}y~iy~|j{|y~|t{}y~ny~}u{|y~|j{|y~}h{|y~|i{|y~}px~ry~}w{}y~w{}y~|s{|y~|ry~}p{|x~|{}y~y" - "{}y~}r{|y~}p{|y~|u{|h~ly~|m{}h~ly~|m{}h~ly~|J{}~}i{}y~w{~|h{|y~|fy~|uy~mv}x~v}|Tx~|wy~U{~|yy~|q{|~or}ly~}xy~|^{" - "|~}Y{~|x{}~}y{}~}w{|~8{}~| v{}~}t{}~}hy~y{}~| yr}h{}~}y{|y~|k{|~}v{|~y|~}ny~r{}~|p{|~}v{|~{|~}m{|y~iy~}t|y~|ny~" - "}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|r{}y~u|y~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~" - "|q{}y~r{|y~|wy~}yy~}r{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|n{|w~}m{|y~}y{}~}u{|y~|s{}y~r{|" - "y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|w{|x~}n{}y~v{}x~|n{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~" - "m{}y~}|w{|y~m{}y~}|w{|y~r{}y~}|w{|n~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{}y~s{}y~" - "o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~pk|p{}y~x{}~|y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{|y~|y{|y~|l" - "{}y~sy~}n{|y~|y{|y~|;{|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}y{}y~}u{|~}s{|~}vy|v{|~}ky~fy~}y{}y~9k~}n{" - "}y~y{~|R{}l~ri~p{|q~}lq~m{|y~sy~|o{|y~sy~|m{}y~x{|y~|j{}y~yy~}o{|y~ Ey~}E{|Qj~j{|~y{|y~}l{|~}xy~|wy~u{|y~|v{|x" - "~{|y~R{}y~a{|y~K{}~|M{}u~|M{|y~hy~}t{}y~i{|y~|f{}y~|_{}y~o{}n~}ey~}n{}y~t{|y~|j{}y~|jy~}t{|y~|e{}y~;{|}v~}jk~}k" - "{|}v~}j{}~}n{|~}wy~ty~wy~t{|o~}o{|y~|t{|y~|px~e{|y~|qy~}p{|y~|e{|y~|gx~py~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{" - "|y~|i{|y~|x{}w~wy~}s{|y~|w{|y~|{y~}qx~p{}y~|q{|y~|gx~p{}y~|r{|y~|v{|y~}c{|y~}jy~}k{}y~r{|y~|m{}y~y{}y~|o{}y~{|~" - "}vy~{|y~|ox~y{|y~|gy~}h{|x~c{}~}a{}~|d{|y~| xy~|u{|y~m{}y~sy~}o{|y~e{|y~s{}~}o{|y~_y~|i{|y~s{}~}n{}y~t{}~}j{|y~" - "d{|y~h{}y~|y~}d{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}y~n{}y~sy~}p{|y~s{}~}k{}y~b{|}w~i{}y~g{}~}t{}~}ly~|y{}y~m{}~" - "}{}~}y{|~}{}~}l{|w~|h{}~}y{}y~h{|y~}d{}y~|d{}~}d{|y~}|m{}t{|}x~}|V{~}w{|x~v{~|r{|y~ty~|l{|y~t{|y~|o{}y~v{}y~m{|" - "y~s{}y~m{}y~}f{}y~t{}~}l{|y~y{}~}iy~|xy~}l{}~}t{}~}qy~}vy~}vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{}y~s" - "y~}p{|y~s{}y~iy~|j{|y~|ty~}o{|y~|ty~}k{|x~g{|y~|hx~q{|y~}r{}y~|x{}y~x{|x~r{|y~|ry~}o{}x~}x~}x~}px~p{}y~|t{}y~}]" - "y~|^{|x~oy|yy~|y{|o{}y~|q{|x~oy|yy~|y{|M{}~}i{}y~w{~|h{|y~|f{}~}v{}~}n{|n~|S{}x~|{}~}Uy}y{}~}qy}p{|r~ky~}y{|y~}" - "_{|~}Yy}x{}~}xy~|xy}8{}~| v{}~}t{}~}hy~y{}~| {{|r~iy~}y{|y~}jy~|w{|~|{|~}o{}~|s{|y~oy~v{|~|{|~}m{|y~j{|o~}o{|o~" - "}o{|o~}o{|o~}o{|o~}o{|o~}s{|p~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|w{|y~|{y~}qx~p" - "{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|o{|x~|y~}my~}{}~}t{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~" - "}i{|q~}m{}y~u{|x~|oy~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~ry~|u{|y~h{|y~e{|y~d{|y~d{|y~d{|y~_{|y~" - "d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~U{|y~y{}~|x{}y~n{}~}t{}~}n" - "{}~}t{}~}n{}~}t{}~}n{}~}t{}~}l{}~}y{}y~k{}y~sy~}m{}~}y{}y~:{|y~vy~|w{}~|s{|y~o{}~|s{|y~{|y~}y{|y~}{}~|s{|y~{|y~" - "}t{}~|s{|y~o{}~|ky~f{}y~yy~}9k~}n{|y~y|~Q{|u~|y}u~r{}t~y}s~o{|s~}k{|s~|m{|y~sy~|ny~sy~ly~}wy~}j{|y~y|y~|ny~| F" - "x~ uj~j{|~x{}y~ly~wy~|wy~u{|y~|u{|x~}~}R{|y~a{}y~K{}~| r{}~}h{}y~t{}y~i{|y~|g{}y~|^{}y~o{}n~}ey~}n{}y~t{|y~|jy~" - "}iy~}t{|y~|ey~}8{|}w~}|mk~}n{|}v~}|hx}m{~}wy~|v{|y~x{|~}t{}n~|p{|y~|t{|y~}p{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|q" - "y~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|x{|x~}wy~}s{|y~|vy~}{y~}q{}y~|qx~p{|y~|g{}y~|qx~q{|y~|u{}y~|d{" - "|y~}jy~}k{|y~|s{}y~|m{|y~|{y~}n{}y~{}~}v{}~y|y~|p{}y~|x{}y~gy~}hx~|c{}~}a{|~}d{|y~| y{|y~t{}y~m{}y~sy~|o{|y~|f{" - "|y~|ty~}o{|y~|`y~|i{|y~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{|x~e{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~" - "|ty~}k{}y~_{|y~}j{}y~g{}~}ty~}l{}y~yy~}m{|y~{y~|y{|y~{y~}m{|y~y}y~h{|y~yy~|hx~by~}d{}~}d{}y~7{}~|y{|~}|~}x{}~q{" - "|y~|v{|y~}l{|y~t{|y~|o{}~}v{}~}m{|y~|t{}y~my~|e{}y~t{}~}ky~|{y~|j{}y~w{}y~l{}~}ty~}qy~}vy~}vy~}s{|y~|y{|y~x{}y~" - "my~|w{|y~|o{|y~x{|y~x{|y~|o{}y~sy~|p{|y~|t{}~}iy~|iy~}ty~|o{}y~s{}y~|lx~|g{|y~|h{|y~}rx~px~|y{}y~y{|x~|r{|y~|ry" - "~}n{|r~}o{}y~|qx~r{}y~}^y~|_{|x~o{|y~|{y~|{}~}o{}y~|s{|x~o{|y~|{y~|{}~}Ny~}i{}y~w{~|h{|y~|f{|y~}|{|}y~|h{}y~L{|" - "v~}T{|~xy~}|yx|x{~|U{}y~y{|y~}`{|~}Y{|~x{}~}x{|y~x{~|8{}~| v{}~}ty~}hy~y{}~| `{|y~}y{|y~|j{}~}v{~}y{|~}p{|y~ry~" - "|p{}~|v{~}y{|~}mx~j{}n~|p{}n~|p{}n~|p{}n~|p{}n~|p{}n~s{}p~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y" - "~|k{|y~|r{|y~}r{|y~|vy~}{y~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~o{|x~y{|y~}n{}y~}~}sx~r{|y~|s{}y~|q{|y" - "~|s{}y~|q{|y~|s{}y~|q{|y~|s{}y~|jy~}i{|s~}|l{}y~sy~}p{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y" - "~s{|y~t{}y~|i{|y~|f{|y~|e{|y~|e{|y~|e{|y~|`{|y~d{|y~d{|y~d{|y~i{|y~|t{}y~n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|" - "t{}y~o{|y~|t{}y~o{|y~|t{}y~ix|j{|y~|}~}w{}y~n{}~}ty~}n{}~}ty~}n{}~}ty~}n{}~}ty~}l{|y~yy~|k{}y~sy~|m{|y~yy~|9{}~" - "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}{~}t{|y~q{}~}q{|y~k{|y~f{|y~y|y~|9x|}y~}q|m{}~x}P{}w~}{}{v~|r{|t~y|}u~" - "|n{}u~}i{|u~}l{|y~sy~|ny~|u{|y~ly~|w{}y~iy~y}y~m{|y~| G{|y~| ry~|xy~|f{|~x{}y~m{}~|wy~|wy~ty~}t{|w~Q{|y~|b{}y~" - "K{}~| ry~|h{|y~|uy~}i{|y~|h{}y~|]x~ns|x~y|e{|y~}n{|y~|u{}y~|k{|y~|iy~}t{}y~e{}y~4{|}w~}|U{|}v~}|Ty~w{}~}v{}y~xy" - "~sy~}ry~}p{|y~|t{|y~}p{|x~p{|r{|y~|s{|x~o{|y~|e{|y~|g{|x~qy~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~|wx~|" - "wy~}s{|y~|v{|y~|y~}q{|x~r{}y~|p{|y~|g{|y~}r{}y~|q{|y~|u{|y~}d{|y~}jy~}k{|y~}sx~ky~}|y~|n{|y~|y~|v{}~y}y~p{|y~}w" - "{|y~}hy~}i{}y~|b{}~}a{|y~d{|y~| y{|y~tx~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~}`y~|hy~}u{|y~}n{}y~t{}~}j{|y~d{|y~h{}y~y{" - "|x~f{|y~m{}y~ty~|u{|y~r{}y~t{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~^{}~}j{|y~g{}y~u{|y~}l{|y~y|y~|ly~|y~wy~|y~|" - "mx~yy~}hy~y|y~hx~a{}y~d{}~}d{}~}6u~y{}y~}y~|py~|v{}y~}l{|y~t{|y~|o{}~}vy~|ly~}ty~}n{|y~|e{}y~t{}~}k{}~y}y~iy~}v" - "y~|m{}y~ty~}qx~w{|x~w{|y~|ry~}y{|y~x{}~}my~|w{|y~|o{|y~x{|y~x{|y~n{}y~|u{|y~|oy~}ty~}iy~|i{}y~u{|y~ny~}s{|y~}m{" - "}y~|f{|y~|g{}y~|t{}y~|p{|w~y}y~y}x~}q{|y~|ry~}ly}x~y}|n{|x~r{}y~|q{}y~}_y~|`{|x~mx~|y~|}y~|n{}y~|u{|x~mx~|y~|}y" - "~|Ny~}i{|y~|x{~|h{|y~|fp~|i{}y~d{}~}d{|x~|S{~}x{}u~|y{}~S{}y~xy~}a{|~}X{~}y{|~|w{}~|{}~7y| u{}y~ty~}hy~y{}~| a{" - "|y~}y{|y~|j{|y~v{}~x{|~}p{}~|s{}~|p{|y~v{}~x{|~}n{}y~|jy~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}ty~}ty~}j" - "{|x~p{|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|v{|y~|y~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~" - "|r{|x~r{}y~|r{|x~r{}y~|p{|x~w{|y~}o{|w~s{}y~|r{|y~}sx~p{|y~}sx~p{|y~}sx~p{|y~}sx~iy~}i{|y~w|h{}y~s{}~}p{|y~tx~n" - "{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~s{|y~tx~}hy~}ey~}dy~}dy~}dy~}`{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~t{}~}ny~}t" - "y~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}j{|x~iy~}~}vy~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}ky~y|y~j{}y~|u{|y" - "~|ly~y|y~7y~}xy~|y{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|k{|y~ey~y}y~6{|y~}b{|x~|O{}y~}y{}{|}y~|p{|w~}{|}" - "{}w~}l{}v~g{}w~}k{|y~sy~|n{}y~uy~}m{|y~v{|y~|j{}w~}l{}y~| Gx~|u{}|Px|O{|y~x{|y~j{|w{|~x{}~}n{|y~v{}~|x{|y~t{}y" - "~}t{}x~Py~|by~}K{}~|dx|Jx|f{|y~fx~v{}y~h{|y~|i{}y~|f{|s{}y~}f{}y~l{|t{|x~l{}y~ux~j{}y~h{}y~|v{|x~f{|y~}hx|dx|a{" - "}v~}Xv~}|bx|m{}~|x{|y~}x{|x~{|y~|t{|y~|r{}y~p{|y~|t{}y~|o{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}ry~}r{|y~|ry~}" - "k{|y~|e{|y~|j{|y~|v{}y~}l{|y~|i{|y~|oy~}s{|y~|uv~}p{}y~}t{}y~}o{|y~|f{}y~}t{}y~}p{|y~|t{}y~|o{}r{}y~|jy~}j{}y~|" - "u{|y~}k{}y~}y~ly~}y~u{|w~}px~u{|y~|iy~}j{}y~}a{}~}`y~|e{|y~| y{|y~|v{}x~m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|ay~|h{}y" - "~u{}y~}n{}y~t{}~}j{|y~d{|y~h{}y~x{|x~g{|y~m{}y~ty~|u{|y~r{}y~t{}~}n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}y~^y~}j{|y~" - "g{|y~|v{}y~}ky~y}y~kw~}w{}w~m{}y~|y{|y~}i{}~}y~}i{}y~|a{}y~d{}~}d{}~}5{}y~}w{|y~}|o{}y~w{|w~l{|y~}u{}y~n{}~}w{}" - "y~k{}y~|v{}y~|my~|e{}y~t{}~}k{|w~}j{}y~u{}~}m{}y~|v{}y~}q{}y~|x{}~}~|x{}y~q{}y~|{|y~y{|y~|my~|w{|y~|ny~}y{|y~x{" - "}y~n{}x~ux~n{}y~|v{}y~|iy~|i{|y~}vy~}o{|y~|rx~n{|y~}e{|y~|fx~|v{}y~}n{|}q~|p{|y~|ry~}j{}y~j{}y~}t{}y~}o{}~}_y~|" - "`{|y~ks~|l{}~|u{|y~ks~|My~}hx~x{~|h{|y~|gx~|}x~}|}y~|j{}y~d{}~}c{|x~S{|~}xy|}y|x{}~|R{}~|x{}~a{|}|X{|~}p{}~| /{" - "}y~|v{}y~}hy~y{}~| a{|~|x{}~|i{}~|vr~|s{|~}s{}~|o{}~|vr~|q{}y~}j{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y" - "~|r{}y~q{|y~|r{}y~u{|y~|ty~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|y~|uv~}p{" - "}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{|x~u{|y~}o{}y~}t{}y~}p{}y~|u{|y~}o{}y~|u{|y~}o{}y~|" - "u{|y~}o{}y~|u{|y~}iy~}i{|y~|e{}y~sy~}p{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~s{|y~|v{" - "}w~|i{}y~|f{}y~|e{}y~|e{}y~|e{}y~|a{|y~d{|y~d{|y~d{|y~h{}y~|v{}y~|n{}y~t{}~}n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y~" - "|n{}y~|v{}y~|n{}y~|v{}y~|j{|x~i{}y~}v{}y~|n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}k{}~}y~}j{}x~ux~k{}~}" - "y~}7{|y~}|{y|{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|j{|y~e{|w~|6y~}`x~I{|~hx|y{|}yw|jw~|" - "f{}x~j{|y~sy~|mx~w|x~l{}~}uy~}j{}w~|k{}y~}| I{|x~}|{y|y~|Py~}O{|~}x{|~}j{}~|y{|~{|}y~|n{}~|v{|y~x{}~}sx~}|y{|}" - "u~Q{}~}by~|K{}~|d{}y~Jy~}f{}~}f{|y~}|{|}y~}kw|y~w|m{}y~}s|my~}w|}w~e{}y~ly~}w|}x~}l{|x~|y{|x~|jy~}h{|x~}|{y|x~|" - "m{~}w|}y~}g{}y~d{}y~_{|}y~}Xy~}|_y~}m{|~}w{|u~y}w~|sx~q{|y~|q{|y~t|}y~}m{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~|e{}" - "x~}v|}x~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|u{}y~}m{|y~q|r{|y~|oy~}s{|y~|u{|w~}o{}x~}|{y|}x~n{|y~|e{}x~}|{y|}x~o{|y~|t" - "{|y~}oy~}x|{y|x~}iy~}j{|x~}w|}x~j{|w~}l{}x~}tw~|q{}y~|t{}y~iy~}k{|x~o|l{}~}`{}~}e{|y~| xx~|y{|}w~m{}w~|y{|x~|lx" - "~}|yy|}|mx~|y{|}x~}m{}y~}|x{|}~}jy~|h{|x~|y{|}x~}n{}y~t{}~}j{|y~d{|y~h{}y~w{|x~h{|y~m{}y~ty~|u{|y~r{}y~t{}~}mx~" - "|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}y~g{}~}|x{|}y~|j{|y~}gx~|y{|}x~}k{}w~}k{}x~}w{|x~}n{|y~|w{}y~|j{|w~|j{}y~|`{}" - "y~d{}~}d{}~} w{|y~}|{|y~}y~}m{|x~}|y{|}y~}n{|~}x{|y~|jx~|y{|}y~}l{}y~}|x{|y}m{}y~t{}~}jw~|jy~}u{|y~|n{}x~}|{|}w" - "~y|s{|x~|{|y~|~}|{}y~}px~y|y~|}y~}ly~|vy~}n{}y~|{|y~y{}y~|n{}w~|y{|x~|mx~|y{|}y~}h{}y~|i{}y~}y{|x~nx~q|}y~|ox~r" - "|m{|y~|iw|x~}x{}y~}w|o{|y}x~y}|n{|y~|ry~}j{}y~i{}x~}|{y|}x~m{|^y~|_{|iu~|j{|s{|iu~|Ly~}h{|y~}|{~|{|}mx|y~}t|o{|" - "y~r{}~}j{}y~d{}~}b{|y~|S{|~}r{}~|Py|w{}:{|~}r{}~|=k|!{}x~}|{|}w~y|jy~y{}~| ay|w{}h{|~}uv|}~}|ry~s{}~|o{|~}uv|}~" - "}|q{|y~}ix~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|r{}y~q{|y~|vx~sy~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~" - "q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}p{|y~|u{|w~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y" - "|}x~ox~s{|y~}pv~}|{y|}x~o{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~hy~}i{|y~|e{}y~{x|y{|}y~|ox~|y{|}w~mx~|y{|}" - "w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~rx~|y{|}y~|}y~}|x{|}~}qx~}|yy|}|m{}y~}|x{|}~}m{}y~}|x{|}~}m{}y~}|x{|}" - "~}m{}y~}|x{|}~}j{|y~d{|y~d{|y~d{|y~gx~|y{|}y~}m{}y~t{}~}mx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}" - "i{|x~i{|x~|y{|}y~}lx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}k{|w~|j{}w~|y{|x~|k{|w~|6{|q~|m{|q~|m{|q~|m{|q~|m" - "{|q~|i{|y~dw~5{}~_{}~}I{}~c{}~d{|y~|dy~|j{|y~sy~|m{|s~|ly~}u{}~}j{|w~i{}m~ S{|s~}Oy~}O{}~|x{}~|j{}q~}n{|~}t{}y" - "~}x~q{}s~}{|y~}R{|y~c{|y~J{}~|dx~Jy~}fy~|e{}t~}k{}q~}no~|nq~}d{}y~lq~|j{|s~|j{|y~|g{|r~|ls~}f{}y~dx~\\y|X{}|]y~" - "}ly~|w{|}y~}|{}~}|r{|y~}py~}q{|p~}k{|q~}q{|p~}|m{|o~|o{|y~|d{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|t{}y~}n{|o~r{|y~|o" - "y~}s{|y~|t{}x~}n{}r~}m{|y~|d{}r~}n{|y~|s{}y~|pp~}hy~}i{|r~}hw~|l{}x~}tw~|r{|y~}s{|y~}jy~}k{}l~|m{}~}`{|y~e{|y~|" - " x{|t~}|y~m{}y~|t~|j{}s~|m{|t~|}~}l{}r~}jy~|g{|t~|}~}n{}y~t{}~}j{|y~d{|y~h{}y~v{|x~i{|y~m{}y~ty~|u{|y~r{}y~t{}~" - "}m{|s~}l{}y~|t~|l{|t~|}~}k{}y~g{}r~}h{}u~k{|t~|}~}k{|w~|k{|x~|w{|x~|ny~}u{}y~iw~io~h{}y~d{}~}d{}~} v{}t~{}x~}o{" - "|q~}ly~}y|y~}i{|s~}j{}s~}m{}y~t{}~}j{}x~j{}y~sy~}n{}~}t~}x~}r{|u~|{t~o{|q~ky~|vw~}ot~}x~}m{}y~|t~|l{|s~}g{|v~j{" - "}t~|o{|k~}p{|o~|n{|y~|is~y{|t~}l{}y~k{|y~|ry~}j{}y~h{}r~}Ny~|Kw~|Lw~|Ky~}g{|r~n{|o~}n{|p{|i{}y~d{}~}ay~|R{|y~}y" - "|{y|}y~| a{|y~}y|{y|}y~|<k~}\"{}~}t~}x~}jy~y{}~| Gy~o{|~}r{}~|ty~|ny~o{|~}q{|y~}i{|y~}py~}s{|y~}py~}s{|y~}py~}s" - "{|y~}py~}s{|y~}py~}s{|y~}py~}w{|y~|so~}q{|q~}o{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|y~|t{}x~}" - "n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}n{|~q{|~p{}~y|r~}m{|r~}l{|r~}l{|r~}l{|r~}gy~}i{|y~|e{}y~{}t~}n{|t~}|y~m{|t~}|y~m{" - "|t~}|y~m{|t~}|y~m{|t~}|y~m{|t~}|y~r{|s~|y{}r~|p{}s~|l{}r~}l{}r~}l{}r~}l{}r~}j{|y~d{|y~d{|y~d{|y~fs~}l{}y~t{}~}m" - "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}Rq~}k{|t~|}~}m{|t~|}~}m{|t~|}~}m{|t~|}~}jw~i{}y~|t~|iw~3{|}w~}|i{|}w~}|i{|}w~}|i{|" - "}w~}|i{|}w~}|g{|~}d{}y~} r{|~|Iy~|dy~|d{|}cy|i{|y}sy}|k{}w~}k{|y~|u{|y~ix~}gy}p~ Q{|}x~}|Ny~}Oy~wy~h{|y}v~}|my" - "~r{|x~}o{|}w~}|x{}y~}Ry~|d{}~}J{}~|dy~}Jy~}g{|y~c{|}x~}|j{}q~}no~|m{|}w~y}|c{}y~l{|y}w~y}f{}w~}hx~dy}x~y}|k{|}w" - "~}|e{}y~dy~} ry~}l{|y~c{}y~|p{}y~q{|r~}|h{|}w~}|o{|s~y}|k{|o~|o{|y~|b{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|s{}y~}" - "o{|o~r{|y~|oy~}s{|y~|t{|x~}l{|}x~y}|l{|y~|b{|}v~}m{|y~|ry~}o{|y}w~y}|gy~}g{|}w~}|g{|x~k{|x~|t{}x~qx~q{}y~|ky~}k" - "{}l~|m{}~}_y~|f{|y~| v{}x~}|{|y~m{}y~{|}x~}|h{|}x~y}|j{}x~}|{}~}k{|}w~y}|iy~|e{}x~}|{}~}n{}y~t{}~}j{|y~d{|y~h{}" - "y~u{|x~j{|y~m{}y~ty~|u{|y~r{}y~t{}~}k{|}x~}|k{}y~{|}x~}|i{}x~}|{}~}k{}y~f{|y}w~}|fy}w~j{|}x~}y{}~}jx~}ix~ux~|o{" - "}y~t{|y~}j{|y~}io~h{}y~d{}~}d{}~} u{|}x~}|y{}y~}o{|y~|}w~}|k{|v~}f{|}x~}|h{|}w~y}|m{}y~t{}~}j{|y~|jy~}s{}y~n{}~" - "}{}x~}y{}~}|q{|}y~}|x{}x~}l{}v~}|jy~|v{|}y~}n{}s~|l{}y~{|}x~}|i{|}x~}|e{|}x~i{|}x~}m{}j~p{|o~|n{|y~|is~y{|t~}l{" - "}y~k{|y~|ry~}j{}y~f{|}x~y}|My~|Jy~|Jy~|Jy~}f{|}u~}n{|o~}O{}y~d{}~}h{}|w{}y~O{|}v~}| ]{|}v~}|:k~}\"{}~}{}x~}y{}~" - "}|jy~y{}~| H{}~|o{|~|s{|~}t{|t~|t{}~|o{|~|q{}y~h{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}" - "y~w{}y~ro~}o{|}w~}|m{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|y~|t{|x~}l{|}x~y}|i{|}x~y}|i{|}x~" - "y}|i{|}x~y}|i{|}x~y}|U{|~}x{|}x~y}|j{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|fy~}i{|y~|e{}y~{|}w~}|k{}x~}|{|y~k{}x~}|{|y~" - "k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~p{}x~}|v{|}w~y}|n{|}x~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|i{|y~d" - "{|y~d{|y~d{|y~e{|}x~}|k{}y~t{}~}k{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|R{}~|{|}x~}|i{|}x~}y{}~}l{|}x~}y{}~}l{|" - "}x~}y{}~}l{|}x~}y{}~}j{|y~}i{}y~{|}x~}|h{|y~} e{|~} V{| .{|~ p{}~}dy~|1{|y~2{}~} U{|y~ ^{}~} ){|y~| {" - "}y~} 6{}~}_{}~}f{|y~| -y~}6{|y~ A{}y~Z{}~} j{|y~I{}y~d{}~}d{}~} \\{|y~a{|}y|*{}~}j{|y~|O{}~}F{|y~K{|}y~y|j{}" - "y~ Ry~}c{|~| r{}~}h{}t~|K{| U{| '{}~}^y~y{}~|O{|~| wy|cy}|st|sy|ay~} ^{|~| 9{| Y{|~| 8y| Q{|y~h{}" - "y~`{|y~ d{|~} c{|~ oy~e{}~}0{}~}2y~| U{}~} ]{}y~|r{|y} 7{}y~ y{}y~| 7{}~}_{|y~f{|y~| .{|y~|6{|y~ " - "A{}y~Z{}~} j{}~}I{|y~d{}~}dy~} \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y~ Ry~}b{~| r{}~}h{|y}x~}| ){}~}^y~y{" - "}~|N{}~ 'y~} ]y~ p{~} g{}~}h{}y~`{}~} h{|}|{}~} c{|~ o{}~}fy~/{}~ c{}~ [{}y~}|v{|}y~} " - " 7x~ x{}y~| 8{}v~M{|v~| .{}y~5{}~} A{}y~Z{}~} k{|y~|I{|y~}e{}~}e{|y~| \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y" - "~ Ry~}b{~| r{}~} i{}~}*{}~| (x~uy| e{}~| q{}~ h{|y~|h{}y~a{|y~| h{|y~|y~| c{|~ ny~" - "g{}~} M{|}q~| 9y|}y~| 1{}w~}M{|v~| 6y}|x{|}y~|6{|y~} A{}y~Z{}~} l{|x~G{}w~}h{}~}h{}w~} [{|y~ g{}~}j{" - "|y~|O{}~}F{|y~J{|y~h{}y~ Ry~}b{~| r{}~} i{}~}-x}y~ '{}x~w|y~| e{}~| qy~ i{|x~g{}y~b{|x~ f" - "{}w~ e{|y}w~}| 8{|w~} ({|n~} m{}s~}7{|w~ @{}y~Z{}~} nv~|F{|y}~}h{}~}h{}~y}| Z{|y~ g{}~}j{" - "|y~|O{}~}F{|y~J{|y~h{}y~ Rx| W{}~} i{}~}-{}y~}| &{}s~| i{|~y}y~ t{|~y}y~ kv~|g{}y~dv~| ex" - "} s{|y~}| '{|n~} m{|y}w~}|6{|y~} ?{}y~Z{}~} nx~}|-{}~} B{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~" - "h{}y~ q{}~} -{|}x~}| f{}y~}| t{|x~}| kx~}|f{}y~dx~}| " - " :{}~} I{" }; - - // Define a 52x64 font (large sans). - static const char *const data_font_large[] = { - " " - " -{| " - " [{|x}|Dw}|Pw}| @{}v~} C{|w}|Ew}|Pv}| xv|Ev|Pu| kv|Dw|P{|v} 6{|w}|E{|x}|P{" - "|w}| pw}| " - " G{|w~}F{}w~P{}v~}Q{}w~}|w{|x~X{|v~vv~|U{|r~| D{}w~F{}w~P{}u~S{|v~|wv~}V{}w~|G{|w~|Q{" - "|u~|Sv~}w{}w~}\"{|}x~}|v{|x~W{}w~|F{}w~Q{|u~}Q{}x~}|v{|x~X{}w~}w{|v~ G{}w~F{}w~|Q{}u~Rv~|w{}w~}O{}w~ " - " " - " E{|w~|H{}w~P{}t~}Ss~}|y{}x~X{|v~vv~|V{|p~ Cw~}H{|w~|Q{|t~}T{|v~|wv~}U{}w~Gw~}Q{|s~|Tv~}w{}w~}#{|s~}|{|x~" - "}V{}w~|H{}w~Ps~}St~}w{}y~}X{}w~}w{|v~ Fw~}H{|w~|Q{|t~}Sv~|w{}w~}P{|w~| " - " D{|w~|J{|w~|Q{|x~}{w~|U" - "{}l~}X{|v~vv~|Vw~}x{}x~} D{|w~|J{|w~|Q{|w~{}x~}U{|v~|wv~}T{}x~}Iw~}Pw~y|w~Tv~}w{}w~}#k~|U{}w~I{|w~|Q{}x~|{}x~|U" - "{}r~|{|x~}X{}w~}w{|v~ Ew~|Iw~}Pw~|}x~}Tv~|w{}w~}Q{|w~| M{| " - " q{}w~Jw~|Q{|x~}xw~Ux~}y{|}t~}W{|v~vv" - "~|W{|x~|v{|x~| D{|w~|Kw~}Pw~x{}x~|V{|v~|wv~}S{}x~}K{}x~}P{}x~|y{|x~}Uv~}w{}w~}${|x~|y{|s~}S{}x~}K{|w~|Q{}x~}xw~" - "Ux~}{|}r~W{}w~}w{|v~ E{|w~|K{}x~}Pw~|y{}x~|Uv~|w{}w~}Qw~| O{}v~} " - " s{}x~}L{}x~|Pw~vw~W{|x~v{|}w~}" - "V{|v~vv~|W{}y~}tx~} C{|w~L{}x~}P{}x~|w{}x~|W{|v~|wv~}R{}x~|M{}x~}P{}x~|w{|x~}Vv~}w{}w~}${|x~v{|}w~|Q{}x~}Lw~|Q{" - "|x~}vw~W{|x~w{|t~|W{}w~}w{|v~ D{|w~L{|x~}P{}x~|w{}x~|Vv~|w{}w~}R{}x~} P{|r~| Y{}w~| " - " A{}x~|N{}x~}P" - "{}x~u{|x~}\"v|vv|V{}y~}t{}y~} B{}x~}N{|x~}P{}x~|u{}x~Vv|vv|P{}x~|O{|x~}P{}x~|u{|x~}Wv|uv| D{}x~|N{}x~|Q{|x~}tx~" - "}X{|x~u{|}y~}|Vu|vv| C{|x~}N{|w~P{|x~|u{}x~Vv|vu|S{|x~} Op~| Zv~| " - " ;v~ u{|v~ 6{|y}|N{|y}|P{|x}s{|x} I" - "{}y~}t{}y~} Aw|Nw|Ow|sw| Qw|Nw|Pw|rx| 5{|x}N{|x}O{|y}|s{|y}| {{|y}| Dv|@v|Rv| C{}x~}x{|w~ Hu|@v|Rw| yv}@v}R" - "{|w} lv|@v|Rv| 8v}@v}|S{|w} m{}w~| E{|y~x}| ;{|w~} " - " vv~| J{}y~}t{}y~} e{}w~}B{|w~}Rv~| Dx~|v{|x~| H" - "v~A{}w~|S{|w~} {{|w~}B{}w~|S{|v~| Ay|sx|Y{}w~|B{|w~}Rv~ 8{|w~}B{|w~}Rv~| o{|w~} ?y}~}| *x~ J{" - "|y~| b{}x~|T{|x~} L{|q~} y{}q~| H{|w~} xw~} `{|w~| {{|}t~)w~}Cv~Lv~Tw~}Dv~ G" - "{|x}w~}Tw~|U{|v~y}| 1{|y}v~y}| cv~y} p{|y}x~y}| {{v|vv| 3{}w~| I{|x~|v{|x~| " - " %{| 5{|y}w~y}|U{}w~|Cv~R{}v~}Q{|}y~}|ux~|Y{|v~|wv~}W{|x~t{}y~} H{|w~}C{}w~|Ru~|S{}w~}w{|v~W{}w~|D{|w~}R" - "t~S{|v~vv~|X{|v~}K{}w~}ux~X{}w~C{|w~}R{}v~}Q{|}y~}|ux~|Y{|v~vv~| J{|w~}D{|w~}R{}u~Rv~|w{}w~}N{|w~}Zw~}Hv~}w{}w~" - "} N{|u~} ,{|y~} Ix|Tx|kw| Ru| 6{|y~|Yv|fx}|Zu| o{|w~Rw~|Hx| Xu| vt|Ns| =t| xt|Ot| [u| ds| kr|" - " Qt| ut| ts| S{|q~} y{}q~| G{}w~| yw~} `{|w~|!{}q~)w~}Cv~Lv~Tw~}Dv~ I{|r~}Tw~|U{|r~} 5{|}o~| yr| " - " ps~} t{|p~| kt| is| s{|y} r{|x}| rx}| bt| lu|S{|v~vv~|!{|y}w~y}| :{|l~|Qx| u{|y}w~}|Q{|x}w~y}|K{|w~| " - " 9y|y}w~|O{|y}w~}|)y|x}dw~|hy|x}dw~|ly|x}y~y}|e{}x~| 6w~}x{}x~} us| lt|Nt|Nt|Nt|Nt| ut|p{}~| 9{|}o~|V{" - "}w~D{}w~R{|t~|S{|u~}vx~|Y{|v~|wv~}W{}y~}t{|x~ G{|w~}E{|w~}R{}t~S{}w~}w{|v~V{}w~E{|w~}R{}t~}T{|v~vv~|W{|v~}s{|y}" - "X{}u~}w{|x~Ww~}Dv~R{|t~|S{|u~}w{|x~X{|v~vv~| I{}w~|Ew~}R{|t~}Sv~|w{}w~}Nw~}Yw~}Hv~}w{}w~} O{|s~|cW} i{}y~|" - "\"{|}L{|u~}|Z{|}v~}|p{}u~}V{|} /g| ({}r~}| v~}R{}x~}vw~}R{|x}|t{|x}|V{|y~|\\{|}t~|i{}x~|]{}q~}|O{}x~}Iw~|R{|" - "w~Hx~ *{|w~V{|}s~}|Sy|y}v~}T{|}q~}|V{|y}p~}|L{|u~}\\{|g~}T{}q~y}|_{}c~}[{|}q~}|U{|}r~}| b{|}q~| w{}v~}X{}k~y" - "}|R{|y}p~}|b{}m~x}y|W{}c~|`{}e~Y{|}o~}|a{}w~}hv~|Y{}w~}M{}w~}W{}w~}k{|u~}b{}w~}V{}t~|h{}t~|h{}u~}jv~^{|}p~}|Z{}" - "m~y}|U{|}p~}|\\{}m~y}y|S{|}o~}y|bZ~g{|v~h{}w~}i{|v~|d{|v~|rv~|l{|v~}kv~|p{|v~|i{}v~g{}v~fv~}g\\~]{|q~}Uw~}I{}q~" - "|P{|w}| w{}w~ yw~} `{|w~|\"o~)w~}Cv~Lv~Tw~}Dv~ J{|q~}Tw~|U{|q~} 7{}l~}\"y}p~y} sr~} v{}n~}R{}v~}V{" - "}c~|_{}d~}^{|}p~}|R{|v~Y{}^~|iv~}r{|v~qv~}a{|}p~}| x{}x~} s{}w~ s{}w~| f{|}r~}|-{}w~|i{|v~({|q~}|W{|v~vv~|Ty|u" - "}y|U{|}o~| ly|u}y|U{|l~|T{|}v~}| {|}p~|T{}p~}|N{|w~} yy|}m~} N{|r~|P{}q~|0{|y}t~|f{}x~}l{|y}t~|f{}x~}l{}p~}h{}" - "x~}%{}v~}N{}v~}N{}v~}N{}v~}N{}v~}Q{|p~W{}\\~}b{|y}p~}|^{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|" - "Z{}u~}jv~^{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|\"{|}q~y}t{}x~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}g{}" - "v~fv~}c{}w~}J{|l~}Vw~}F{}w~|R{}x~|w~Ss~}x{|x~X{|v~|wv~}W{}y~}t{|x~ F{|w~|Fw~}R{|x~y}x~}T{}w~}w{|v~U{}x~}Fw~}R{|" - "w~{w~|U{|v~vv~|V{}v~|x{|}v~Y{|s~}x{}x~W{|w~}F{}w~Qw~|w~Ss~}x{|x~X{|v~vv~| H{}w~F{}w~Qw~|}x~|Tv~|w{}w~}O{}w~Xw~}" - "Hv~}w{}w~} P{|q~c{}Y~} ix~!y~|N{}r~}\\{}r~}s{|q~|Y{|y~} 5{|}e~} *{}m~|\"v~}R{}x~}vw~}Rw~|tw~|V{|y~|]{}q" - "~}k{|w~^{|l~|Q{}x~}J{}w~P{}x~}Ix~ *{}x~}W{}n~|Zy|}p~}W{|}k~}Z{}i~|Nt~}\\{|g~}V{}l~|`{}c~}\\{}l~|X{}n~} e{|l~" - "|Ty|y}u~y}|Rt~X{}g~}V{|}j~}d{}g~}|Z{}c~|`{}e~\\{|}i~}|d{}w~}hv~|Y{}w~}M{}w~}W{}w~}l{|u~}a{}w~}V{}t~}i{|s~|h{}t~" - "|kv~`{|k~}|\\{}i~}|Z{|k~}|^{}i~}|W{|h~}dZ~g{|v~h{}w~}hv~}d{}v~q{}w~}l{}u~kv~|o{}v~j{|v~|fv~}h{}v~f\\~]{|v~u}U{}" - "w~Iu}v~|Qt~| w{}x~} {{w~} `{|w~|#{}o~)w~}Cv~Lv~Tw~}Dv~ Ov| s~x}|Tw~|U{|x}s~| 9{}j~}%{}j~| uq~| x{}l" - "~}St~V{}c~|_{}d~}`{|}k~|T{|v~Y{}^~|iv~}r{|v~qv~}c{|k~}| {}v~} t{}w~ t{}u~| i{|l~-v~i{}w~|Xw}|R{|l~X{|v~vv~|W{|" - "}o~}|X{|m~| p{|}o~}|X{|l~|U{}r~}!{|n~}U{}n~|Ow~} {{|}j~} N{|r~|R{|n~}1{|r~|g{|w~k{|r~|g{|w~k{}n~iw~$t~Nt~Nt~Nt" - "~Nt~P{|r~V[~}d{|}j~}`{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}t~|kv~`{|k~}|Z{|k~}|Z{|k~}|Z{|k~}" - "|Z{|k~}|&{|k~}w{|w~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}fv~}h{}v~b{}w~}K{}j~}W{|w~|H{|w~|R{|x~}{|x~}U{|" - "x~}|w~}|{}x~X{|v~|wv~}W{|x~t{}y~} E{}w~G{}x~}Qw~|{}x~|U{}w~}w{|v~Tw~}H{}w~Q{}x~|{|x~}U{|v~vv~|U{}v~|}t~}Y{}x~|{" - "}w~y|x~}V{|w~|H{|w~|R{}x~|{|x~}U{}x~}|w~}{|x~}X{|v~vv~| G{}x~}H{|w~|R{}x~|yw~Tv~|w{}w~}P{|w~|Xw~}Hv~}w{}w~} " - "P{}w~y|w~|d{|Y~| j{|y~}\"{}x~Oo~}_{|o~}u{|o~}Zw~| 8{}b~} ,{|j~}#v~}R{}x~}vw~}Rw~sw~U{|y~|^{}o~}lw~|_{}k~|Q" - "{}x~}Jw~|P{|w~|Jx~ *w~|Xk~|[m~}X{}h~}[{}h~}P{}t~}\\{|g~}X{|j~|`{}c~}^{|i~}[{|j~ gi~|X{|}l~}|V{}t~|Y{}e~|Y{}f" - "~}f{}d~}\\{}c~|`{}e~]{}e~}|f{}w~}hv~|Y{}w~}M{}w~}W{}w~}m{|u~|`{}w~}V{}s~|j{}s~|h{}t~}kv~b{|g~}]{}g~}]{|g~}_{}g~" - "}Y{}f~dZ~g{|v~h{}w~}h{}v~dv~}q{}w~}lt~|m{|v~mv~}kv~}e{|v~|j{|v~|f\\~]{|w~}O{|w~|D{|w~|Rr~| ww~} w~} `{|w~|${|v~" - "}|#w~}Cv~Lv~Tw~}Dv~ Ov~ !{|v~}Nw~|O{|v~} :{|u~}|w{|}v~|'{}i~| r{|}v~} y{}v~}|x{|}v~}U{}t~|W{}c~|_{}d" - "~}a{}g~|V{|v~Y{}^~|iv~}r{|v~qv~}e{|g~}\"{}t~} u{}w~ u{}s~| >y~}P{|k~-{|w~}k{|w~}Ww~|S{|k~X{|v~vv~|Y{|}k~}|Z{|y~" - "}y|xy|}w~| s{|}k~}|Z{|l~|V{}p~}\"{|y~}|w{|}w~|V{|}|u{|v~P{}x~} {{}h~} N{|~y}y|}x~|S{|v~}|y{|}w~}2{|w~y}x~|g{}x" - "~|k{|w~y}x~|g{}x~|kx}|w{|}w~}k{}x~}%{}t~|P{}t~|P{}t~|P{}t~|P{}t~|P{}t~}W{|[~}e{}f~}b{}c~|a{}c~|a{}c~|a{}c~|X{}w" - "~}M{}w~}M{}w~}M{}w~}Z{|d~}|`{}t~}kv~b{|g~}]{|g~}]{|g~}]{|g~}]{|g~}){|g~|{|w~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f" - "{|v~h{}w~}f{|v~|j{|v~|b{}w~}L{|u~}|w{|}v~|W{|w~|Iw~}Qw~x{}x~|V{}y~}x{}s~|X{|v~|wv~}Vx~}v{|x~| D{}x~}I{}w~Q{}x~|" - "xw~U{}w~}w{|v~T{|w~|J{|w~Q{|x~}x{|x~|V{|v~vv~|T{}q~}|Wx~|x{}s~T{|w~I{|w~|R{|x~}x{}x~|Vx~}x{}s~|X{|v~vv~| Fw~}J{" - "|w~|R{|x~}x{|x~}Uv~|w{}w~}Q{|w~|Ww~}Hv~}w{}w~} Pw~}y{|x~}cY~ i{}y~|#{|w~}Qm~|`m~}w{|m~|\\{}v~| ;{}`~} -" - "{|r~x}t~}$v~}R{}x~}vw~}S{|w~t{|x~}U{|y~|_{|w~}w{}w~|n{}x~}_{|t~w}u~|Q{}x~}K{}w~N{}x~}Jx~ +{|w~Xs~y}s~|\\m~}X{}" - "f~\\{}g~}R{|s~}\\{|g~}Y{|i~|`{}c~|_{|s~w}s~}]{|s~x}s~ hr~}r~|[{|f~}Xs~}Y{}d~|\\{|c~}g{}b~|^{}c~|`{}e~_{|a~|g{" - "}w~}hv~|Y{}w~}M{}w~}W{}w~}n{|u~|_{}w~}V{}s~}jr~|h{}s~|lv~c{|p~}q~}^{}f~}_{|p~}q~}`{}e~[{}q~}p~dZ~g{|v~h{}w~}h{|" - "v~|f{|v~p{|v~m{|t~}m{}w~}m{|v~|m{}v~c{}v~jv~}e\\~]{|w~}Nw~}D{|w~|Sp~| ww~|!w~} `{|w~|${}w~}!w~}Cv~Lv~Tw~}Dv~ " - " Ov~ !{}w~}Mw~|N{|v~ :{}v~|s{|v~V{|t}|V{|t~s}w~| p{|v~ {{|v~|t{|v~|Vs~}W{}c~|_{}d~}c{|d~|W{|v~Y{}^~|iv~" - "}r{|v~qv~}f{|p~}q~}${}r~} v{}w~ v{}q~| ?y~}Ps~x}u~,v~k{}w~|Ww~|Su~}v|}w~X{|v~vv~|Z{}v~}y|wy|}v~}[{|}q{}x~} t{}" - "v~}y|wy|}v~}&{}w~|x{|w~}#y|r{}x~}Kw~|R{|w~ {{}p~}v|x~} H{}x~|S{}w~t{}w~|3x|x{}x~|h{|x~}j{|}|x{}x~|h{|x~}`{|w~l{" - "|w~$s~}Ps~}Ps~}Ps~}Ps~}Pr~W{}[~}g{|c~}c{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}s~|lv~c{|p~}q~}_" - "{|p~}q~}_{|p~}q~}_{|p~}q~}_{|p~}q~}+{|p~}q~}w~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}e{}v~jv~}a{}w~}Lu~r{" - "|v~V{|w~J{}x~}Q{}x~|w{}x~Vx~|w{}u~}Vv|vv|U{}x~}x|}w~ Bw~|K{|w~|R{|x~}w{|x~}Vu|vv|S{|w~K{|w~|Qx~}v{}x~Uv|vv|T{|}" - "t~}|Tx~|w{|u~|S{}x~}Jw~}Qw~vw~Vx~|w{}u~}Vv|vv| Dw~|Kw~|Qw~v{}x~|Vv|vv|Pw~|Vw~}Hv|uv| G{|t}|P{|t}|P{|t}|P{|t}|P{" - "|t}|Lw~|xw~c{|[~} iy~}\"u~|S{|l~a{}l~|x{}l~]{}t~ ={|^~} .{|u~}|u{|}w~}$v~}R{}x~}vw~}S{}x~}t{}x~}Xy|y}y~y}x" - "|cw~}u{}w~o{|w~^u~}t{|}y~|Q{}x~}Kw~|N{|w~|T{}sx~s{} 4{}x~}Y{}v~}|v{}u~\\m~}X{}v~y}|wy|s~]{}x~}x|v{|}t~}Sr~}\\{" - "|v~k|Z{|t~}|v{|y}y~|`h|u~^t~|u{|}u~|^u~}|v{|}v~} iv~y|v{|t~]{|o~y}p~|[{|r~|Z{}w~}q|}s~]{|s~}|t{|}u~}g{}w~}r|y" - "}q~}_{}w~}h|_{}w~}j|`{|s~}|s{|}t~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}o{}u~|^{}w~}V{}r~k{|r~|h{}r~lv~d{|t~}|uy|s~_{}w~}" - "s|y}t~}a{|t~}|uy|s~a{}w~}s|y}s~]{}u~}|ty|}v~dn|}v~}n|g{|v~h{}w~}gv~}f{}w~}ov~|n{|t~}mv~|l{}v~|o{|v~|bv~}l{}v~dc" - "|u~}]{|w~}N{}w~D{|w~|T{}o~| x{|w~!w~} `{|w~|${}w~ w~} >w~}Dv~ Ov~ !{}w~|Mw~|M{}w~ :v~|q{}w~|Xp~}X{}v~|p{|" - "}| o{}w~| v~|r{|v~W{|r~|X{}v~}i|^{}w~}h|d{|s~}y|xy|}s~}[{|y}u~y}y|]{}w~}h|v~|iv~}r{|v~qv~}g{|t~}|uy|s~&{}p" - "~} w{}w~ w{}o~| @y~}Q{}v~}|u{|}y~,{|w~}m{|w~}Vw~|T{|v~|s{|}~({|w~}|o{|}w~|P{}x~| w{|w~}|o{|}w~|(x~}tw~ rw~K{}x" - "~|Rw~ {{}o~}w{|x~} H{}x~|T{|w~r{}x~}-{}x~|hw~|d{}x~|hw~|_{}x~|mw~|%{|r~|R{|r~|R{|r~|R{|r~|R{|r~|R{}r~|Y{|v~|y{|" - "v~}h|h{|s~}|t{|}u~}c{}w~}h|`{}w~}h|`{}w~}h|`{}w~}h|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}r~lv~d{|t~}|uy|s~a{|t~" - "}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~-{|t~}|u{|}q~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}dv~}l{}v~`" - "{}w~}M{|v~p{}w~|V{}x~}L{}x~}Q{|x~|ux~}Wx~|v{|w~} {{}q~| Aw~|Lw~|Qw~u{}x~| y{|x~}Lw~|Q{}x~tx~}#{|}r~}Rx~u{|}y~}|" - "Q{}x~}L{}x~}Q{}x~|v{|x~}Wx~|v{}w~} j{|w~L{}x~}Q{}x~|u{}x~ x{}x~}Uw~} b{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|" - "P{|w~|xx|av~|fv~| j{|y~|#{}t~Sk~|c{|k~}y{|k~}_{|s~} ?{}t~}y| u{|u~|p{}y~}$v~}R{}x~}vw~}Sw~|tw~|[{|}m~}|h{" - "|w~sw~|p{}x~|_{}v~|q{|}|Q{}x~}L{}w~Lw~}U{}y~|ux~u{|y~}U{|x}| `w~|Z{|v~}s{|v~}]w~y}y|{}w~}X{}x~|p{|u~|^y}|n{|u~" - "|U{}x~y}w~}\\{|w~}K{|u~}o{}|Mv~|_{}v~}q{|u~_{}v~}r{|v~| jy~}|qu~|_{}t~}y|s{|}t~}\\{}w~}w~}Z{}w~}o{|u~}_{|t~|n" - "{|}x~}g{}w~}n{|}t~}`{}w~}L{}w~}P{|t~}m{|}w~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}p{}u~|]{}w~}V{}w~}w~|l{}r~|h{}r~|mv~e{|" - "u~}|p{|t~`{}w~}q{|}u~|c{|u~}|p{|t~b{}w~}p{}u~|_{|u~|n{|}y~W{|v~|Z{|v~h{}w~}g{|v~fv~|o{}w~}n{}x~}w~mv~|kv~}ov~}a" - "{|v~|n{|v~|M{}v~}\\{|w~}N{|w~|E{|w~|U{}v~}{|u~| x{|x~}\"w~} `{|w~|$v~ w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{|w~" - "}p{|w~}Xn~|Zv~ _{|v~ !{|w~}p{}w~}X{}w~}w~}W{}v~|M{}w~}R{|t~|p{|t~|_{|}l~}|`{}w~}hv~|iv~}r{|v~qv~}h{|u~}|p{|" - "t~({}n~} x{}w~ x{}m~| Ay~}R{|v~}p{}+{}w~|nv~Uw~|T{}w~| x{|w~|k{|w~|Q{|x~| x{|w~|k{|w~|*{|x~rx~|R{|w}Fw~Kw~|S{}" - "x~| {|n~}w{|x~} H{}x~|T{}x~}qw~|.{}x~|i{}x~}c{}x~|i{}x~}^{}x~|n{}x~}${}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w" - "~}Rv~|w~}Y{}w~}x{|v~U{|t~|n{|}x~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}r~|mv~e{|u~}|p{|" - "t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~/{|u~}|p{}t~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}d{|v" - "~|n{|v~|`{}w~}M{}w~}ow~}U{}x~|N{|w~Px~}t{|x~|Xx|sy| w{}s~| @{|w~M{}x~|Q{}x~|tw~ x{}x~}N{}x~|Q{|x~|t{|x~|&{}t~}v" - "~} t{}x~|N{|x~}Q{|x~}t{}x~|Xx|sy| g{|x~}N{|x~}Q{|x~}sx~} {{|x~}Tw~} d{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|R{|w~Z{}w~}" - "g{}w~} Ay|J{}y~#{|s~}Tk~}c{}j~|{}j~_q~| A{}u~} q{}v~|n{}~}$v~}R{}x~}vw~}Sw~t{|w~\\{|h~|i{}x~}s{}x~}q{|x~}^" - "v~|C{}x~}Lw~}L{}w~V{|v~|wx~w{|v~|V{}w~ a{|w~Yv~}q{|v~|^{}y|u{}w~}Xy}|m{|u~M{|v~}V{|w~|}w~}\\{|w~}Ku~|?{|v~^u~o" - "{}v~|a{|v~}p{}v~ j{~|nv~}`u~}|l{|}u~]v~{v~Z{}w~}mu~_u~}j{|y~}g{}w~}l{|}u~}a{}w~}L{}w~}Q{|u~}i{|}y~|g{}w~}hv~|" - "Y{}w~}M{}w~}W{}w~}q{}u~|\\{}w~}V{}w~|w~}lw~|v~|h{}q~mv~f{|u~}m{|u~}a{}w~}o{}v~}d{|u~}m{|u~}c{}w~}o{|u~_{}v~|j{|" - "W{|v~|Z{|v~h{}w~}fv~|h{}v~n{}w~}nw~|w~|o{|v~j{|v~}q{}v~_{}v~nv~}M{|u~[{|w~}Mw~}E{|w~|V{}v~}x{|u~| vw~} `{|w~|$" - "w~} w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{}w~|ow~}Xm~|[v~ ^v~| \"v~|p{|v~Xv~{v~V{}v~|N{}w~}Ru~}l{}u~|b{|g~}" - "|b{}w~}hv~|iv~}r{|v~qv~}i{|u~}m{|u~}*{}l~} y{}w~ y{}k~| By~}R{}v~ y{|w~}o{|w~}Uw~|T{}w~ x{|x~}g{}x~|R{|x~} y{|" - "x~}g{}x~|+{}y~}r{}y~}R{}w~Fx~}M{|}w~ Mm~}w{|x~} H{}x~|Tw~p{}x~|.{}x~|j{|w~b{}x~|j{|w~]w~n{|w~#v~{v~Rv~{v~Rv~{v~" - "Rv~{v~Rv~{v~S{|w~}{}w~|Zv~|x{|v~Uu~}j{|y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}q~mv~f{|" - "u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}1{|u~}m{|u~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" - "~}c{}v~nv~}_{}w~}Mv~n{}w~Tw}N{|x}P{|x}r{|x} F{|}x~}| ={|x}|O{|x}|Px}|s{|x}| xw|Nw|Pw|rw|'{|v~}|y{|v~} tw}Nw}P{|" - "x}rx}| 6w|Nw|Ox|rw| Nw~} e{}h~}\\{}h~}\\{}h~}\\{}h~}\\{}h~}S{|w~Z{|v~gv~| Ay~}L{|y~}${|q~}V{|j~ci~}|i~|a{}p~|" - "Oy|Uw|jw|Vu|Wv|kw|b{}v~} p{|v~|l{|}$v~}R{}x~}vw~}T{|x~}t{|x~}]{|g~|i{}x~|s{|w~qw~|^v~B{}x~}M{|w~|L{|w~}V{|}" - "w~}xx~x{}w~}|U{}w~ a{}w~Z{|v~o{}w~}U{}w~}X{|j{}v~|M{}v~Vw~}{}w~}\\{|w~}L{|v~|>v~}_{|v~|nv~}a{}v~nv~| \\{}w~}" - "b{|u~|h{|}v~|`{|w~}{}w~|[{}w~}m{|v~|a{}v~}gy}g{}w~}j{}u~|b{}w~}L{}w~}Q{}v~}f{|~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}r{}" - "u~|[{}w~}V{}w~y|w~m{|w~{v~|h{}w~}v~|nv~f{}v~}ju~|b{}w~}nu~d{}v~}ju~|d{}w~}n{}v~|`v~}D{|v~|Z{|v~h{}w~}f{}w~}hv~}" - "n{|v~o{|w~{}x~}o{}w~}i{}v~|s{|v~|^v~}p{}v~M{|u~|[{|w~}M{}x~}E{|w~|W{}v~|v{|u~| ww~} `{|w~|$w~} w~} >w~}Dv~ " - "Ov~ !v~Lw~|M{|w~| <{}w~|ow~}Xy~}w|}t~[v~| _{}w~} #{|w~}n{}w~|Z{|w~}{}w~|Vu~|O{}w~}S{}v~}j{}u~c{}d~|c{}w~" - "}hv~|iv~}r{|v~qv~}i{}v~}ju~|,{}v~y}w~|v~} {{}w~ {{}v~y}w~|u~| Cy~}R{}w~}R{|ey|_{}w~|pv~Tw~|T{}w~ y{|x~}e{}x~|\\" - "{|}p~} {{|x~}e{}x~|,{}y~}r{}y~}R{}w~G{}x~|Rq~| N{|m~}w{|x~} H{}x~|U{|w~p{|x~}.{}x~|j{}x~|b{}x~|j{}x~|_{|w~|n{}" - "x~|${|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{}w~|{|w~}[{|v~w{|v~V{}v~}gy}c{}w~}M{}w~}M{}w~}M{}w~" - "}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~}v~|nv~f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|c{}d{}|d{}v~}" - "k{}u~|f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}bv~}p{}v~^{}m~y}|Yv~o{|}w~ Py~}|u{|v~} 2w~} f{" - "}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~T{|w~Yv~|i{|v~ A{}x~}M{}y~|$o~|W{|j~ch~}i~}" - "b{}n~T{|}t~y}|Zw~}kw~}X{}u~|X{}w~|m{}w~|d{|v~| ov~}j{|$v~}R{}x~}vw~}T{}x~}t{}x~}]u~}|{|y~|y{|y}x~|iw~|rw~r{" - "}x~}]v~B{}x~}Mv~Jv~T{|}w~|{x~{|w~}|S{}w~ aw~}Z{}w~}o{|v~U{}w~}Ev~}M{|v~W{}w~y{}w~}\\{|w~}Lv~}>{|v~|_{|v~m{}w~}" - "av~|n{|v~ 8{|y}6{|~|4{}v~c{|v~}d{|v~`{}w~|{|w~}[{}w~}lv~|b{|v~}e{|g{}w~}i{}u~b{}w~}L{}w~}R{|v~}dy|g{}w~}hv~|Y{}" - "w~}M{}w~}W{}w~}s{}u~Y{}w~}V{}w~|{w~|nw~}{v~|h{}w~y|v~nv~g{|v~}i{|u~b{}w~}n{|v~|f{|v~}i{|u~d{}w~}n{|v~|a{|v~C{|v" - "~|Z{|v~h{}w~}f{|v~|j{|v~|mv~|p{|w~{|x~}ov~|hv~}sv~}]{|v~|r{|v~|Mu~|Z{|w~}M{|w~E{|w~|X{}v~|t{|u~| xw~} `{|w~|$w" - "~} w~} >w~}Dv~ Ov~ !w~}Lw~|M{|w~| <v~nw~}X{|s{}v~}\\{}v~| `{|v~ #{}w~|n{|w~}Z{}w~|{|w~}Uu~|P{}w~}T{|u" - "~h{}v~}f{|r~y}v~}r~}d{}w~}hv~|iv~}r{|v~qv~}j{|v~}i{|u~-{}v~}{}w~{|v~} {}w~ {}v~}{}w~{|u~ Cy~}Rv~|S{}~}g{|y~|_v~" - "q{}w~|Tw~|T{}w~| {{x~}t{|y}u~}|u{}x~^{}m~} {{x~}wq}y|s{}x~,{}y~}r{}y~}R{}w~H{|x~}Qs~} L{}m~}w{|x~} H{}x~|U{|x~" - "}p{|x~}.{}x~|k{|x~}a{}x~|k{|w~cx}u~|n{|x~}#{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}Tv~xv~[v~}w{|v" - "~W{|v~}e{|c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~y|v~nv~g{|v~}i{|u~g{|v~}i{|u~g{|v~}i{" - "|u~g{|v~}i{|u~g{|v~}i{|u~d{}y~f{}y~|f{|v~}k{|s~f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}b{|v~|r{|v~|^{}i~}|" - "\\v~q{}t~| F{}v~| C{~| mw~} gu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|V{|w~Y{}w~}i{}w~} B{" - "}w~}Mx~${}n~W{|k~}d{|U~}c{|m~}W{|n~}[w~}kw~}Xt~}X{|w~}mv~cv~| o{|v~| mv~}R{}x~}vw~}Tw~|tw~|^{}v~|x{|y~|u{|~" - "|iw~|rw~s{|w~\\v~B{}x~}N{|w~}J{}w~|S{|n~|Q{}w~ b{|w~|Zv~|nv~|V{}w~}E{}w~}M{|v~X{|w~|y{}w~}\\{|w~}M{|v~={}v~^{|" - "v~m{}w~}b{|v~lv~| <{|}x~}6{|x~}|7{}w~}cv~|b{|w~}b{|v~xv~[{}w~}l{}w~}bu~|P{}w~}h{}v~}c{}w~}L{}w~}Ru~M{}w~}hv~|Y{" - "}w~}M{}w~}W{}w~}t{}u~X{}w~}V{}w~|{}x~}o{|w~|{v~|h{}w~|{v~}ov~gu~|h{}v~|c{}w~}mv~}fu~|h{}v~|e{}w~}mv~}a{|v~C{|v~" - "|Z{|v~h{}w~}ev~}j{}v~l{}w~}p{}x~}{|w~ov~|h{|v~}u{}v~[{}v~rv~}M{}v~}Y{|w~}Lw~|F{|w~|Y{}v~|qu~| Kt|Uw~}uu|Mt|Ru|u" - "{|w~|Wt|Ow~}Mu|Tw~}uu| Jw~}Dv~Tu|mv|Vu|Pt|Ku|Qu|Bv|Us|Rv~ !w~}Lw~|M{|w~| iv|Sv~o{|w~}N{}v~\\{|t~}|Is|Mu| u{}" - "w~| Zt| Lv~|n{|v~[{|v~xv~Tu~P{}w~}T{}v~|gu~g{|t~}|y{|v~x{}t~}e{}w~}hv~|iv~}r{|v~qv~}ju~|h{}v~|/{}v~}y{}w~y{|v" - "~}!{}w~!{}v~}y{}w~y{|u~ F{|}y~}x|V{|v~S{}x~}i{|w~|`{}w~|rw~}Sw~|T{|v~|!{}y~}u{|n~}v{}y~}a{|k~} {}y~}vn~}t{}y~}" - "-{}y~}r{}y~}R{}w~I{|w~Pt~}| L{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|kw~|a{}x~|kw~|ct~}lw~|${|v~xv~U{|v~xv~U{|v~xv~U" - "{|v~xv~U{|v~xv~U{|w~}x{}w~|]{|v~v{|v~Wu~|L{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{|v~}f{}w~|{v~}o" - "v~gu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|f{}w~h{}w~|gu~|l{|r~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~" - "h{}w~}a{}v~rv~}]{}g~}]w~}s{|r~|Xt|Nt|Nt|Nt|Nt|Nt|Xt|lu|Ut|Pt|Nt|Nt|Nt| 0{}v~|Pu|Pt|Nt|Nt|Nt|Nt| ut|t{}y~} nw" - "~}uu| t{}w~}|wv|v{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~V{|w~Xv~iv~| C{|v~M{|y~}%{}m~}Wk~}d{|U~}d" - "{|k~}Y{}k~|]w~}kw~}Y{|s~X{|v~n{|w~}d{}w~} n{}w~} lv~}R{}x~}vw~}U{|w~t{|w~]v~|w{|y~|`w~|rw~s{}x~|\\v~|C{}x~}" - "N{}w~|J{|w~}Q{|r~|O{}w~ b{}w~Z{|v~m{}w~}V{}w~}E{}w~}M{|v~Xw~}x{}w~}\\{|w~}M{}w~}=v~}^{|v~m{}w~}b{|v~lv~} ?{|}u" - "~}6{|u~}|:{}w~}d{}w~|`{|w~}c{}w~|x{}w~}\\{}w~}l{}w~}c{|v~}O{}w~}gu~c{}w~}L{}w~}S{|v~}M{}w~}hv~|Y{}w~}M{}w~}W{}w" - "~}uu~}W{}w~}V{}w~|{|w~|p{}w~yv~|h{}w~|{|v~ov~h{|v~}fu~c{}w~}mv~}g{|v~}fu~e{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}e{}v~j" - "v~|l{}w~}pw~|yw~|q{|v~f{}v~|w{|v~|Zv~}t{}v~M{}v~}X{|w~}L{}x~}F{|w~|Z{}v~|o{}v~| P{|}q~}|Xw~}w{}s~}|S{|}q~}|X{}s" - "~}|x{|w~|Z{|}r~}|W{}k~}W{}s~}|x{|w~|`w~}w{|s~}|Rv~Lv~Tw~}n{|v~}Xv~_w~}w{}s~}r{|s~}cw~}w{|s~}|V{|}r~}|Yw~}w{}s~}" - "|V{}s~}|x{|w~|Zw~}w{}t~|Y{}o~}|Z{}i~]{|w~|m{}w~|c{|v~iv~i{}w~|pu~ow~}hv~}m{|v~|d{|v~iv~`d~Uw~}Lw~|M{|w~| l{|s~" - "}|u{}x~}av~o{|w~}M{}w~|\\{}q~}|P{}o~}|\\w~}w{|s~}|^x~y}hv~W{}w~}X{|w~|m{}w~|d{}w~}h{}w~}]{|y}w{|}x~}|]_~|dv~t{}" - "w~t{|w~}[{|q~}|U{|y}i~}f{|`~b{|v~lv~|\\{}w~|x{}w~}U{|u~Q{}w~}U{|v~}f{|v~|ht~|w{|v~v{}u~}f{}w~}hv~|iv~}r{|v~qv~}" - "k{|v~}fu~/{|w~}x{}w~x{|w~}I{|T{}w~S{|i{|\\w~}x{}w~x{|w~|!v~}O{|}p~}|Y{|v~T{|v~}k{|v~}_v~s{}w~|Sw~|Su~|#{|x~u{}l" - "~ux~|bv~}y|v{|x~} !{|x~ul~|ux~|.{|x~|t{|x~|R{}w~J{|w~|L{|}x~}&{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|l{" - "}x~}`{}x~|l{}x~}br~|o{}x~}Qv~|S{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{|w~}]{}w~}v{|" - "v~X{|v~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|{|v~ov~h{|v~}fu~i{|v~}fu~i{|v~}fu~i{|v~}" - "fu~i{|v~}fu~g{|u~j{}v~}h{|v~}l{|w~}v~}g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`v~}t{}v~\\{}f~}^w~}t{}v~}y|Y" - "{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|_{|}q~}|r{|}r~}[{|}q~}|W{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Qv~Lv~Lv~" - "Lv~O{|y}w~}u~|\\w~}w{|s~}|V{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Q{}u~Q{|}r~}|x{}x~}b{|w~|m{}w~|a{|w~|m{}w~|a{" - "|w~|m{}w~|a{|w~|m{}w~|c{|v~iv~aw~}w{}s~}|^{|v~iv~ W{}w~}u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{" - "}w~}W{}w~X{}w~}k{|v~ C{|v~|M{}y~|&{|k~}X{}l~|cU~}di~|[{}i~|^w~}kw~}Y{}s~|Xv~|o{}w~|dw~} mv~| lv~}R{}x~}vw~}" - "^{}Z~f{|w~}v{|y~|`w~|rw~t{|x~}[{}w~}C{}x~}Nv~Hv~O{}v~}M{}w~ bw~}Z{}w~}m{|v~V{}w~}E{}w~}M{|v~Y{}w~w{}w~}\\{|w~}" - "Mv~|>{|v~]{|v~m{}w~}b{|w~}l{}w~}W{|v}M{}v~D{}r~}6{|r~}|>{|v~|e{}w~|^{|w~|dv~w{|v~\\{}w~}lv~|c{}v~N{}w~}g{}v~|d{" - "}w~}L{}w~}S{}v~L{}w~}hv~|Y{}w~}M{}w~}W{}w~}vu~}V{}w~}V{}w~|yw~}pw~}yv~|h{}w~|y{}w~}pv~h{}v~e{}v~|d{}w~}mv~}g{}v" - "~e{}v~|f{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}dv~|l{|v~k{|v~q{|w~x{}x~}q{}w~}e{}v~wv~}Y{|v~|v{|v~|N{|v~}W{|w~}L{|w~F{|" - "w~|[{}v~l{}v~ S{|}k~|Zw~}y{|o~}V{|k~|\\{|o~}y{|w~|\\{|m~}X{}k~}Y{|o~}y{|w~|`w~}y{|o~}Sv~Lv~Tw~}o{|v~}Wv~_w~}y{|" - "o~|v{|o~|ew~}y{|o~}Y{|}n~}|[w~}y{|o~}Y{|o~}y{|w~|Zw~}y{|r~|[{}j~[{}i~]{|w~|m{}w~|b{}w~|k{|w~}i{|w~}q{|u~|q{|w~|" - "h{|v~|o{|v~}b{}w~|k{|w~}`d~Uw~}Lw~|M{|w~| n{|o~}vw~|av~o{}w~|M{|v~[{|o~}|U{}k~}]w~}y{|o~}_u~|k{|w~}Wu~X{|w~|m{" - "}w~|dv~|h{|v~_{}x~}x{}s~}__~|dv~t{}w~t{|w~}\\{}n~}Y{|}e~}f{|`~b{|w~}l{}w~|\\v~w{|v~T{|u~R{}w~}U{}v~dv~}i{}u~u{|" - "v~u{|u~|g{}w~}hv~|iv~}r{|v~qv~|k{}v~e{}v~|c{~}I{|y~}w{}w~w{|y~}I{}~|U{}w~T{}~|k{}~|\\y~}w{}w~w{|y~| v~}P{}k~Z{|" - "v~S{|v~}x{|}v~}|y{|v~}^{|w~}u{|w~}Rw~|S{|u~}${}y~|v{}v~}|wy|}y~u{|y~}c{|x~}r{|x~}Q{|q{| W{}y~|uw~vy|v~u{|y~}-w~" - "|v{|w~Q{}w~K{|w~|I{|w~'{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|x~}]{|q{|X{}x~|m{|w~_{}x~|m{|w~]{|}w~}q{|w~Pv~|Sv" - "~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~W{|v~vv~^{|v~|v{|v~X{}v~J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z" - "{|v~g{|v~}g{}w~|y{}w~}pv~h{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|g{|u~l{}v~}g{}v~kw~}{}v~g{|v~h{" - "}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`{|v~|v{|v~|\\{}w~}s|y}t~}_w~}u{|v~|Y{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|" - "}k~|d{|}k~|v{|m~}_{|k~|[{|m~}W{|m~}W{|m~}W{|m~}Rv~Lv~Lv~Lv~Q{|}l~\\w~}y{|o~}Y{|}n~}|X{|}n~}|X{|}n~}|X{|}n~}|X{|" - "}n~}|S{}u~S{|}n~}{|x~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{}w~|k{|w~}aw~}y{|o~}^{}w~|k{|w~} X{|w~}" - "t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}ly|y{|w~}f{|w~}h{|w~}X{}x~}X{|v~kv~| Cv~|Lx~&{|i~|Y{|m~}bU~|e{}" - "h~\\{|u~}|xy|}u~^w~}kw~}Yr~}X{}w~}ov~d{}w~ lv~| lv~}R{}x~}vw~}^{}Z~f{|w~|v{|y~|`w~|s{|w~tw~|[{|v~|D{}x~}Nw~" - "}H{}w~|Q{|t~|N{}w~ c{|w~|Zv~|lv~|W{}w~}E{}w~}M{}w~}Z{|w~|w{}w~}\\{|w~}N{|v~={}w~}\\v~|nv~|b{}w~}l{}v~W{}v~M{}v" - "~G{|}p~|6{|o~}@u~e{|w~|\\{}w~e{|w~}v{}w~|]{}w~}m{|v~|cv~}N{}w~}g{|v~}d{}w~}L{}w~}Sv~}L{}w~}hv~|Y{}w~}M{}w~}W{}w" - "~}x{|u~}U{}w~}V{}w~|y{}w~q{|w~|yv~|h{}w~|y{|v~pv~hv~}e{|v~}d{}w~}mv~}gv~}e{|v~}f{}w~}mv~}a{|v~|D{|v~|Z{|v~h{}w~" - "}d{}w~}l{}w~}jv~|r{|w~x{|x~}qv~|e{|v~}y{}v~W{}v~vv~}N{|u~V{|w~}Kw~|G{|w~|\\{}w~}j{}v~ T{}i~}[w~}{}m~}X{}j~|]{}m" - "~}{|w~|]{}j~Y{}k~}Z{}m~}{|w~|`w~}{|l~Tv~Lv~Tw~}p{}v~}Vv~_w~}{|m~|x{|m~|fw~}{|m~}[{|j~|\\w~}{}m~}[{}m~}{|w~|Zw~}" - "{|q~|\\{}i~[{}i~]{|w~|m{}w~|b{|w~}k{}w~|hw~}q{|u~}q{}w~|g{}v~ov~}a{|w~}k{}w~|`d~Uw~}Lw~|M{|w~| Gy|l{|Z{}m~}x{|w" - "~`v~p{|v~Kv~Z{|m~|X{}j~}]w~}{|l~`t~|l{}w~|X{|u~}Y{|w~|m{}w~|e{}v~f{}w~}b{|v~}y{|q~}`_~|dv~t{}w~t{|w~}^{|k~}[{|c" - "~}f{|`~b{}w~}l{}w~}]{|w~}vv~|T{|v~}S{}w~}Uv~}d{}v~j{|u~t{|v~t{|u~g{}w~}hv~|iv~}r{|v~r{|v~|kv~}e{|v~}dx~}I{|}v{}" - "w~v{|}I{}x~|V{}w~U{}x~|m{}x~|\\{|v{}w~vy| {{v~}R{|i~Z{|v~R{|v~}|q~}|v~}\\v~u{}w~Qw~|R{|t~|'{|y~}v{}w~}p{|t{}y~|" - "d{}x~|r{|x~}Ry}r{|~ X{|y~}tw~sw~|u{}y~|.{|w~}x|}w~|Q{}w~L{|w~|G{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|w~p{|x~}]" - "{~|r{|}Y{}x~|mw~|_{}x~|m{}x~|[{|w~|r{}x~|Pv~|T{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{}w~}" - "v{}w~}_{}w~}u{|v~Xv~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fu~g{}w~|y{|v~pv~hv~}e{|v~}jv~}e{|v~}" - "jv~}e{|v~}jv~}e{|v~}jv~}e{|v~}f{|u~n{}v~}fv~}l{}x~}y{|v~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}_{}v~vv~}[" - "{}w~}q{|}u~|`w~}uv~W{}i~}[{}i~}[{}i~}[{}i~}[{}i~}[{}i~}e{}i~}x{}k~}a{}j~|\\{}j~Y{}j~Y{}j~Y{}j~Sv~Lv~Lv~Lv~R{}j~" - "}]w~}{|m~}[{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|T{}u~T{|f~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{|w~}k{}w~|a" - "w~}{}m~}_{|w~}k{}w~| Xw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}l{|y~}y{}w~fw~}f{}w~X{}x~}Wv~|m{|v~ C{}w~}" - "[{|}|o{|y~|&g~|Y{}n~|b{}V~e{|g~}]v~}r{|v~}_w~}kw~}Z{|r~}X{|v~p{|w~}dw~} pw|v~l| {{v~}R{}x~}vw~}^{}Z~f{|w~|v" - "{|y~|`{}x~}s{|x~}u{}x~}Y{}v~|E{}x~}O{|w~}H{}w~|S{|}r~}|P{}w~ c{|w~Yv~|lv~|W{}w~}Ev~|N{|v~|Zw~}v{}w~}\\{|w~}|}v" - "~y}|X{}w~}>{|v~|\\{}w~}o{|v~a{}w~}l{}v~W{}v~M{}v~J{|}p~}|2{|}p~}|D{}v~|e{}x~}p{|}w~}|vx|uw~|f{}w~|v{|w~}]{}w~}m" - "{}v~c{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|u~}T{}w~}V{}w~|y{|w~|r{}x~}xv~|h{}w~|x{}w~" - "}qv~i{|v~|dv~}d{}w~}mv~}h{|v~|dv~}f{}w~}n{|v~|`u~D{|v~|Z{|v~h{}w~}d{|v~m{|v~|j{}w~}r{}x~}x{|w~qv~|d{}v~y|v~|Vv~" - "}x{}v~Mu~|V{|w~}K{}x~}G{|w~|]{}w~}h{|v~ U{}u~v}s~}\\w~}|v~w}t~}Zr~v}v~|^{}t~w}v~}|w~|^{}t~v}t~Zv}v~s}[{}t~w}v~}" - "|w~|`w~}|u~x}t~}Uv~Lv~Tw~}q{}v~|Uv~_w~}|v~x}s~y{|v~x}s~fw~}|u~x}t~}]{|s~x}s~|]w~}|v~w}t~}]{|t~w}v~}|w~|Zw~}|t~}" - "x~|]{}t~u}u~[{|x}v~q}]{|w~|m{}w~|av~kv~g{}w~q{}t~qv~e{}v~q{}v~_v~|m{|v~_d~Uw~}Lw~|M{|w~| J{|}v~}r{}v~}|_{}u~w}u" - "~|y{}x~}`v~q{|v~}K{}w~|\\{}w~}p~}Z{}s~w}u~}]w~}|u~x}t~}as~m{|v~W{}t~Y{|w~|m{}w~|ev~|f{|v~c{|u~}yn~a_~|dv~t{}w~t" - "{|w~}_{|t~w}t~}]{|b~}f{|`~b{}w~|l{}w~}]{}w~|v{|w~}S{|v~}T{}w~}Uv~|d{|v~|k{}v~|t{|v~s{}v~|h{}w~}hv~|i{}w~}r{|v~r" - "{|v~|l{|v~|dv~}ev~}C{}w~C{}v~|W{}w~V{}v~n{|v~|W{}w~ sv~}S{|s~}y~x}v~Z{|v~Q{|e~}[{|w~}w{|w~}Qw~|R{}r~|){}y~|w{|w" - "~}g{|y~}dw~q{}x~}S{}~}s{}y~ X{}y~|tw~s{}x~}u{|y~}-{}p~}P{}w~M{|w~|F{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|Tw~p{}x~" - "|]y~}s{|y~Z{}x~|n{|x~}^{}x~|n{|w~Y{|x~}s{|x~}Ov~|T{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}Xv" - "~u{|v~_v~|u{|v~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|x{}w~}qv~i{|v~|dv~}k{|v~|d" - "v~}k{|v~|dv~}k{|v~|dv~}k{|v~|dv~}e{|u~p{}v~}f{|v~|m{}w~wv~}h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^v~}x{}v" - "~Z{}w~}o{}v~}`w~}v{|w~|W{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}f{}u~v}s~}{s~w}t~}cr~v}" - "v~|]{}t~v}t~[{}t~v}t~[{}t~v}t~[{}t~v}t~Tv~Lv~Lv~Lv~S{}h~|^w~}|u~x}t~}]{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~" - "|\\{|s~x}s~|U{}u~U{|s~x}q~|`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|av~|m{|v~`w~}|v~w}t~}_v~|m{|v~ X{|w~" - "r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~l{|w~}yw~}h{|w~dw~}Y{}x~}W{}w~}m{}w~} Xg|}v~s|e{|}x~}o{}y~&{}f~Y{|o" - "~}a{|V~f{|e~}_{|w~}p{|v~_w~}kw~}Z{}w~}v~Wv~|q{}w~}e{|w~ pc~} {{v~}R{|x}|v{|x}|^{}Z~f{|w~|v{|y~|`{|w~s{}x~}v" - "{|w~Wu~|F{|x}|O{}w~|H{|w~}U{|}w~|x~|w~}|R{}w~ c{}x~}Yv~|lv~|W{}w~}F{|v~N{|v~}Z{}w~u{}w~}\\{|k~}Z{}w~}x{|}u~y}|" - "L{}v~Zv~|pv~}a{|v~l{}v~|X{}v~M{}v~M{|}p~}|,{|}p~}|H{}v~|e{|w~q{|q~}y{}x~|v{|x~}fv~tv~]{}w~}n{}v~|c{|v~|N{}w~}f{" - "}v~d{}w~}L{}w~}T{}v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}{|u~}S{}w~}V{}w~|xw~}rw~|xv~|h{}w~|x{|v~|rv~i{|v~|d{}v~d{}w~}n" - "{|v~|h{|v~|d{}v~f{}w~}n{}v~|`{}v~}|F{|v~|Z{|v~h{}w~}cv~|n{}v~i{}w~}rw~|ww~|s{|v~b{}q~}U{|v~|{|v~|N{}v~|U{|w~}K{" - "|w~G{|w~|^{}w~}f{|v~ V{}y~}|r{|u~|]r~|u{|u~}\\{}u~}s{|}y~|_{|u~|u{|}s~|_{}v~}|t{}v~}Vw~}T{|u~|u{|}s~|`r~|u{|u~|" - "Vv~Lv~Tw~}ru~|Tv~_r~|v{|}v~}{w~|u{}v~}gr~|u{|u~|^u~}|v{|}u~]r~|u{|u~|_{|u~|u{|}s~|Zr~}|v{|\\v~}|r{|}y~Wv~S{|w~|" - "m{}w~|a{}w~|m{|w~}g{}w~|rs~qw~}dv~}s{|v~|_{}w~}m{}w~|Nu~Uw~}Lw~|M{|w~| K{}r~u{|r~}a{|v~}|v{}v~yw~|`v~r{|u~|K{|w" - "~|]{}w~|xy|}t~}[u~}|s{|}~}]r~|u{|u~|ay|v~|n{}w~|X{|s~|Z{|w~|m{}w~|f{|v~dv~|e{|u~}|{|v~y|}v~}bx}u~q}u~x}|dv~t{}w" - "~t{|w~}_u~|u{|u~|_{|u~}|v{|}t~v}f{|q}u~p}b{}w~|l{|v~]v~tv~R{}v~}U{}w~}V{|v~|cv~}l{|v~}s{|v~s{|v~}h{}w~}hv~|i{}v" - "~r{|v~r{|v~|l{|v~|d{}v~fu~|C{}w~C{|u~|X{}w~W{}v~}m{}v~|X{}w~ sv~}T{|u~}|yy~}x{|}y~Z{|v~P{|g~}Y{}w~|xv~Pw~|T{|v~" - "}u~}*x~v{}w~ex~dw~qw~}U{|x~}t{}x~ Xx~sw~s{}x~}tx~,{|r~|O{}w~N{|w~|Dw~({|w~|m{}w~|a{|m~}w{|x~} H{}x~|T{}x~}qw~|]" - "x~}t{|x~|\\{}x~|nw~]{}x~|nw~|Xw~sw~|Ov~|Tv~tv~Xv~tv~Xv~tv~Xv~tv~Xv~tv~Y{|w~}tv~|a{|v~t{|v~Y{|v~|J{}w~}M{}w~}M{}" - "w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|x{|v~|rv~i{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{" - "}v~d{|u~r{}v~}e{|v~|n{}w~v{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^{|v~|{|v~|Z{}w~}nu~`w~}v{}w~V{}y~}|r" - "{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|g{}y~}|r{|o~}|u{|}v~}e{}u~}s{|}y~|^{}v~}|" - "t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}Uv~Lv~Lv~Lv~T{}u~}|v{|}v~}^r~|u{|u~|^u~}|v{|}u~\\u~}|v{|}u~\\u~}|v" - "{|}u~\\u~}|v{|}u~\\u~}|v{|}u~U{}u~Uu~}|u{}u~|_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{}w~}m{}w~|`r~|u{" - "|u~|`{}w~}m{}w~| Xw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|m{|u~y{|w~hw~|d{|w~Y{}x~}Vv~mv~| XZ~}g{}t~oy~}'{}" - "e~}Y{}p~_W~|fc~|`v~n{}w~|`w~}kw~}Zv~|}w~|X{}w~}qv~|e{}x~} q{|c~| {{v~} y{|x~}t{}x~}]{|w~}v{|y~|_w~|u{|w~|vw" - "~|Wt~ p{}w~|H{|v~V{}w~}yx~y{}w~}S{}w~ cw~|Z{|v~k{}w~}W{}w~}Fv~}Qy|u~}Z{|w~|u{}w~}\\{|i~|\\v~|y{}p~}|Nv~}Z{|v~|" - "s{|v~}`{|v~lu~|X{}v~M{}v~P{|}p~}|b{|Z~}b{|}p~}|L{}v~}d{}x~|r{|n~{}x~|uw~|h{}w~}t{}w~|^{}w~}q{|}u~}b{}v~M{}w~}f{" - "}v~d{}w~}L{}w~}T{}v~K{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|x{|w~s{}w~wv~|h{}w~|w{}w~}rv~i{}v~c{}v~d{}w~}n{" - "}v~|h{}v~c{}v~f{}w~}o{|u~_{|t~}|H{|v~|Z{|v~h{}w~}c{}v~nv~}i{|v~s{|w~|w{}x~}s{}w~}b{|q~S{}v~|v~}N{}v~}T{|w~}K{|w" - "~|H{|w~| s{}|m{}w~}]t~}q{}v~|^{}v~}ny|_u~q{}t~|`{|v~|q{|v~|Ww~}Tu~q{|t~|`t~}r{|v~}Vv~Lv~Tw~}t{|u~Rv~_t~}r{}v~}" - "y~}r{}v~gt~}r{|v~}_{}v~|r{|v~}^s~q{}v~_{}v~|r{}t~|Zs~T{|w~}m{|Wv~S{|w~|m{}w~|a{|w~}mv~|g{|w~}s{|s~|s{|w~|d{|v~|" - "u{|v~}]v~mv~N{}v~Tw~}Lw~|M{|w~| L{}p~w{|p~}bv~}s{}w~y|w~_v~wx|}t~}J{|w~}^{}w~r{}u~|]{|v~|Ot~}r{|v~}_{|v~nv~W{}s" - "~}Z{|w~|m{}w~|f{}w~}d{}w~}eu~}x{|w~|x{}v~|`{|w~}q{|w~}`v~t{}w~t{|w~}`{}v~q{}v~_u~}r{|v~}V{|w~}Wv~|l{|v~^{}w~}t{" - "}w~|R{}v~}V{}w~}V{|v~bv~}l{|v~|s{|v~r{}v~h{}w~}hv~|i{}v~r{|v~r{}v~k{}v~c{}v~gu~|B{}w~B{|u~|Y{}w~X{}v~}k{}v~|Y{}" - "w~ sv~}Tu~|wy~}u{|Z{|v~O{|u~}|x{|}v~}_{|p~}y{|p~}Ww~|Tw~}y{|t~|,y~}vw~|e{}y~dw~|s{}w~}V{|w~}u{}w~ Xy~}sw~s{}x~}" - "t{}y~*y}x~}|[m|}w~l|^{}w~C{|x~}({|w~|m{}w~|`m~}w{|x~} H{}x~|T{|w~|s{}x~}\\w~}u{|w~|]{}x~|o{}x~}]{}x~|o{}x~}Ww~t" - "{}x~}Nv~|U{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~|t{|w~}av~}t{|v~Y{}v~I{}w~}M{}w~}M{}w" - "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|w{}w~}rv~i{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~c{|" - "u~t{}v~}d{}v~n{|w~|v{|v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}]{}v~|v~}Y{}w~}n{|v~|aw~}vv~V{}|m{}w~}]{}|m" - "{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}g{}|m{}r~|q{|v~|g{}v~}ny|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~" - "|q{|v~|Vv~Lv~Lv~Lv~U{|v~}q{|v~|_t~}r{|v~}_{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}V{}u~V{}v~" - "|r{|v~}_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`v~mv~_s~q{}v~_v~mv~ X{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~" - "|x{|u~|x{}x~|j{|w~m{|u~|x{}x~|j{|w~b{}x~|Z{}x~}V{}w~|o{|v~ WZ~}gx~}w~|q{}y~|({|c~}_v|{}r~u|d{}X~f{}b~|b{|w~}mw~" - "}`w~}kw~}[{|v~{}w~}X{|w~}r{|v~d{}x~| q{}c~ yv~} y{}x~}t{}x~}\\v~}w{|y~|_{}w~|vw~}v{|x~}X{|r~ qv~Fv~X{}w~}|x" - "x~x{|}w~}U{}w~ d{|w~Y{|v~k{}w~}W{}w~}G{}v~|Xm~}Y{}x~}t{}w~}\\{|h~}]v~y|l~}P{|v~|Y{|u~u|}v~}_{|v~|n{|u~|X{}v~M{" - "}v~R{|o~}|`{|Z~}_{|}p~}|P{}v~}cw~r{|l~}x~|u{|x~|hv~|t{|v~^{}e~}a{}v~M{}w~}f{|v~|e{}d~|_{}g~|d{}v~K{}^~|Y{}w~}M{" - "}w~}W{}p~|Q{}w~}V{}w~|ww~|tw~}wv~|h{}w~|vv~|sv~i{}v~c{|v~|e{}w~}o{|u~g{}v~c{|v~|g{}w~}p{|u~|^{}q~y}|M{|v~|Z{|v~" - "h{}w~}c{|v~|p{|v~gv~|t{|w~v{|x~}sv~|a{|s~|Rq~}N{}v~}S{|w~}Jw~}H{|w~| bv~|^t~ov~}^v~}P{|v~|p{}u~|`v~|o{|v~Ww~}U" - "{|v~o{}u~|`u~}p{|v~Vv~Lv~Tw~}u{|v~}Qv~_u~}pt~}pv~|hu~}p{|v~`{|v~|p{|v~|_t~ov~}a{|v~|p{}u~|Zt~S{}w~Gv~S{|w~|m{}w" - "~|`v~|o{|v~ev~s{|x~y}x~}s{}w~|c{}v~uv~}\\{}w~|o{|w~}O{}v~|U{|w~}Lw~|M{|w~} M{|x~}x|}w~}xv~}x|}x~|d{}v~qw~y}x~}_" - "v~x{}q~}I{|w~}_{|w~|q{|u~]{}w~|Nu~}p{|v~^{}w~|p{|w~}X{|q~Z{|w~|m{}w~|fv~|d{|v~f{|v~}w{}w~|wu~`{|w~}q{|w~}`v~t{}" - "w~t{|w~}a{|v~ov~}a{|v~}p{}v~|W{|w~}Wv~}l|}v~^v~|t{|v~Q{}v~}W{}w~}V{|v~b{}w~}l{}v~r{|v~r{}v~|i{}w~}hv~|i{|v~|s{|" - "v~r{}v~k{}v~xi~}y{|v~|iu~|A{}w~A{|u~|Z{}w~Y{}v~}i{}v~|Z{}w~ sv}|U{}v~|vy~}S{|v~O{|w~}s{|v~_{|o~|{o~}Ww~|U{}x~}v" - "{}u~}.{|y~|w{|w~d{|y~|e{}w~t{}v~}W{|v~|v{}w~}cY|8{|y~|sw~sw~|t{|y~| `{|Z~}_{}x~}C{|w~}({|w~|m{}w~|`{|n~}w{|x~} " - "H{}x~|Sv~|u{}w~|\\{}v~v{|v~|^{}x~|p{|w~\\{}x~|p{|w~W{|x~}u{|w~Mv}|Uv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~" - "Zv~rv~b{|v~s{|c~l{}v~I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|vv~|sv~i{}v~c{|v~|l{}v~c{|v" - "~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|c{|u~v{}v~}c{}v~o{|w~|u{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\q~" - "}X{}w~}mv~}aw~}vv~Ev~|Mv~|Mv~|Mv~|Mv~|Mv~|Ws~|o{}w~}gv~}Ov~|o{|v~_v~|o{|v~_v~|o{|v~_v~|o{|v~Vv~Lv~Lv~Lv~Uv~}o{}" - "w~}_u~}p{|v~`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|Wt|W{|v~|q{}u~|`{|w~|m{}w~|a{|w~|m{}w~|" - "a{|w~|m{}w~|a{|w~|m{}w~|`{}w~|o{|w~}_t~ov~}`{}w~|o{|w~} X{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|yu~|w{|x~}j{}x~}" - "mu~|w{|x~}j{}x~}b{|x~}Z{}x~}V{|v~o{}w~} WZ~}g{}|yw~}qx~'a~|c{|}t~}k~}|fY~}g{}`~b{|w~|m{}w~`w~}kw~}[{|w~}{|v~Wv~" - "r{}w~}dw~| lv~| kv~| yw~|tw~|\\{}v~}|y{|y~|^v~}y|}v~uw~X{|p~ rv~Fv~Xw~|vx~v{|w~U{}w~ d{}x~}Y{|v~k{}w~}W{}w" - "~}H{|v~}Wo~}|Y{|w~|t{}w~}\\{|v~x}|x}s~}^v~|j~}Q{}w~}V{}l~}]v~}n{}u~}X{}v~M{|v}U{|}p~}|]{|Z~}\\{}o~|S{}v~}c{|x~}" - "rv~}|w{|}t~|tx~}i{|v~rv~|_{}h~}|_v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~|P{}w~}V{}w~|w{}w~u{|w~|" - "wv~|h{}w~|v{}w~}sv~iv~}c{|v~|e{}w~}p{|u~|gv~}c{|v~|g{}w~}sy|}u~}\\{}m~}|Q{|v~|Z{|v~h{}w~}bv~}p{}w~}g{}w~}t{}x~}" - "v{|w~sv~|`{}u~}Q{|r~|O{|u~R{|w~}J{}w~H{|w~| b{|w~}^u~|o{|v~_{}v~Ov~}nu~|a{}w~}m{}w~|Xw~}Uv~|nu~|`u~nv~|Wv~Lv~T" - "w~}v{}v~}Pv~_u~o{}u~|p{}w~}hu~nv~|a{}w~}n{}w~}_u~|o{|v~a{}w~}nu~|Zu~|S{}w~Gv~S{|w~|m{}w~|`{}w~}o{}w~}e{}w~s{}x~" - "}|w~sv~a{}v~w{}v~[{|w~}ov~|P{}v~|T{|w~}Lw~|M{|w~}:{|4x~|v{|w~}{}x~}u{}x~dv~}q{}s~|_v~x{}r~}S{|y}~y}|w{|w~}_w~}o" - "{|v~}^{}w~Mu~nv~|_{|w~}pv~|X{}w~}v~|[{|w~|m{}w~|g{|v~bv~|g{}v~v{}w~v{|v~|a{|w~}q{|w~}`v~t{}w~t{|w~}a{}w~|o{|v~a" - "{}v~nv~}W{|w~}W`~_{|v~rv~|Q{}v~|X{}w~}V{|v~b{}w~}lu~r{|v~r{|v~|i{}w~}hv~|hv~}s{|v~rv~}kv~}xi~}y{|v~|ju~|@{}w~@{" - "|u~|[{}w~Z{}v~}g{}v~|[{}w~ Gv~}uy~}S{|v~Ow~}q{|w~|`{|n~}o~}Ww~|Uw~|t{}u~|0{|y~|w{|x~}d{|y~|e{|v~}w|t~}X{|v~|vv~" - "}c{|Z~}8{|y~|sw~t{}w~s{|y~| `{|Z~}`{}x~}M{|~}|v{|}v~'{|w~|m{}w~|_{}o~}w{|x~}Vv}| s{}x~|S{|v~}|{y|}w~}Z{}v~|w{|v" - "~}_{}x~|pw~|o{}w~m{}x~|p{}x~|vy|}w~y}|g{|w~|u{}x~|o{}w~3{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{}w~}" - "r{}w~|c{}w~}s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|v{}w~}sv~iv~}c{|v~|lv~}c{|" - "v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|b{|u~x{}v~}bv~}p{|w~}t{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\{|r~|" - "X{}w~}mv~}aw~}v{}w~}F{|w~}M{|w~}M{|w~}M{|w~}M{|w~}M{|w~}W{|u~}m{}w~h{}v~O{}w~}m{}w~|a{}w~}m{}w~|a{}w~}m{}w~|a{}" - "w~}m{}w~|Wv~Lv~Lv~Lv~V{}v~n{|v~_u~nv~|a{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~},{}w~}q{}t~}`" - "{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`{|w~}ov~|_u~|o{|v~`{|w~}ov~| X{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|u" - "u~|u~|v{|w~j{}x~|nu~|v{|w~j{}x~|b{|w~Zw~}Uv~|q{|v~ VZ~}c{}w~r{|y~}({}`~d{}^~|h{|Z~g{|_~}c{}w~l{|w~`w~}kw~}[{}w~" - "|yv~|X{}w~|sv~|dV~} 2v~| k{}w~| {{|w~t{|w~Zs~y}y~|^{|o~|v{}x~}rx|e{|v~y}u~n{|w~},{|v~Fv~|Y{|~}tx~t{}~|U{}w~ " - " dw~|Y{|v~k{}w~}W{}w~}Hu~Vp~}|Y{|w~}s{}w~}\\{|~}|q{}t~|`{|q~}|xy|t~|Rv~|U{|}p~|[{}v~|ot~} V{|}p~}|Z{|Z~}Z{|}p~}" - "|W{}v~|b{}x~|s{}w~|s{|u~|tw~i{}w~}r{}w~}_{}g~}|`v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~O{}w~}V{}" - "w~|w{|w~|v{}w~vv~|h{}w~|uv~|tv~iv~}c{|v~|e{}w~}sy|s~fv~}c{|v~|g{}f~}Z{}k~}S{|v~|Z{|v~h{}w~}b{|v~pv~|g{}w~}tw~|u" - "w~|u{|v~_{}u~O{}t~|O{|u~|R{|w~}J{|w~|I{|w~| aw~}^v~}m{}w~}`v~|P{|v~m{}v~|av~l{|w~}Xw~}V{|v~m{|v~|`v~}n{}w~|Wv~" - "Lv~Tw~}w{}v~}Ov~_v~}o{|v~}o{|w~}hv~}n{}w~|av~|n{|v~|`u~mv~|bv~m{}v~|Zv~}R{}w~Gv~S{|w~|m{}w~|`{|v~ov~d{}w~|tw~|{" - "w~|u{|w~}`v~}y{|v~|Z{}w~|q{|v~P{}v~|Sv~|Lw~|Lv~|W{|y}w~}|iy}5{|y~}sw~|x~}s{}y~|f{|v~|ps~^v~x{}q~}|W{|r~|y{|w~}`" - "{}w~m{}v~^{}w~Mv~}n{}w~|^{}w~q{|v~Wv~y|w~}[{|w~|m{}w~|g{}v~b{}w~}h{|v~|v{}w~u{}w~}a{|w~}q{|w~}`v~t{}w~t{|w~}av~" - "mv~|c{|v~|n{|v~W{|w~}W`~_{}w~}r{}w~}Q{|v~}X{}w~}V{|v~b{}w~}lv~}r{|v~r{|v~|i{}w~}hv~|h{}v~s{|v~s{|v~|kv~}xi~}y{|" - "v~|ku~|?{}w~?{|u~|\\{}w~[{}v~}e{}v~|\\{}w~ H{}v~ty~}S{|v~P{|w~o{}w~_s|}r~s|Vw~|V{|w~r{|u~0{|y~v{}x~}d{|y~|d{}o~" - "|x~}Y{}v~v{|v~|b{|Z~}8{|y~rw~u}v~|s{|y~| `{|Z~}a{}l~|X{|m~|'{|w~|m{}w~|^o~}w{|x~}W{|v~| xm~}W{|n~}X{|v~|vv~}e{}" - "n~}v{}x~}o{|v~m{}x~|q{|w~w{|o~|t{|~}y|w{|}v~u{|x~}o{|v~3{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w" - "~}r{}w~}\\v~|r{|w~}cv~|s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|uv~|tv~iv~}c{|v" - "~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|a{|u~|}v~}av~}pw~}s{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}[" - "{}t~|W{}w~}mv~}aw~}v{}v~|Fw~}Lw~}Lw~}Lw~}Lw~}Lw~}Vu~l{|w~|iv~|Ov~l{|w~}av~l{|w~}av~l{|w~}av~l{|w~}Wv~Lv~Lv~Lv~V" - "v~|mv~|`v~}n{}w~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|-v~|r{|x~}v~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{" - "}w~|a{|w~|m{}w~|_{}w~|q{|v~^u~mv~|`{}w~|q{|v~ Ww~p{}w~pw~jw~yd|yw~jw~t{|p~|tw~jw~nu~|tw~jw~pv~}qw~Zw~|U{}w~}q{}" - "w~} F{}w~}W{|w~|s{}y~|){|_~}f{}\\~|h{}\\~|g{}^~c{}w~l{|w~|aw~}kw~}[v~x{}w~}X{|w~}t{|v~cV~} 2v~| k{}w~| {{|x~" - "}t{|x~}Z{|o~}y|`{|}r~|v{|w~t{}u~}|hv~}y{}u~o{|w~|,{|v~F{}w~|X{|sx~s{|T{}w~ e{|w~X{|v~k{}w~}W{}w~}Iu~|Vm~|[{}w~" - "r{}w~}L{}u~`{|r~|s{|u~S{}v~V{|}m~}|\\u~p{}t~} Y{|}p~}|VY|W{|}p~}|[{|v~|aw~rw~}q{|v~|t{}x~iv~q{|v~_{}e~}av~}M{}w" - "~}f{|v~|e{}d~|_{}g~|dv~}m{}n~|h{}^~|Y{}w~}M{}w~}W{}q~}P{}w~}V{}w~|vw~}vw~}vv~|h{}w~|u{}v~tv~iv~}bv~|e{}e~|fv~}b" - "v~|g{}g~}X{|}k~}U{|v~|Z{|v~h{}w~}av~|r{|v~f{|v~u{|w~|u{}x~}u{}w~}`{|t~|O{}v~}Nu~|Q{|w~}Iw~}I{|w~| a{}w~^v~|m{|" - "w~}a{|v~O{|w~}lv~|b{|w~}kv~Xw~}V{|w~}lv~|`v~|n{|w~}Wv~Lv~Tw~}x{}v~|Nv~_v~|nv~|nv~hv~|n{|w~}b{|v~lv~|`v~}m{|w~}c" - "{|w~}m{|v~|Zv~|R{}w~|Hv~S{|w~|m{}w~|_{}w~|q{|w~}d{|w~}u{|w~y{}x~|u{|w~|`{|v~y|v~}Y{|w~}q{}w~|Q{|v~}S{}v~Kw~|L{}" - "w~}Y{|p~}|n{|y~}5{}y~r{|t~qy~}f{}v~ot~}^v~x{}o~}Y{}p~|{|w~|`w~}lv~|_{|w~}Nv~|n{|w~}^{|w~|r{}w~|X{}w~}yv~[{|w~|m" - "{}w~|gv~}b{}v~h{|v~u{}w~u{|v~a{|w~}q{|w~}`v~t{}w~t{|w~}b{|w~}m{|w~}c{|v~lv~|X{|w~}W`~_v~|r{|v~Qu~W{}w~}V{|v~b{}" - "w~}lv~}r{|v~qv~|i{}w~}hv~|h{|v~|t{|v~s{}v~jv~}xi~}xv~|lu~[|]{}w~\\\\|u~|]{}w~\\{}v~}c|u~|]{}w~ H{}w~}ty~}X{}g~|" - "[{}x~}nw~Vs~|Nw~|V{}x~}pv~}1{}y~v{}x~}d{|y~}c{}r~}{|x~}Z{}w~}v{|v~|a{|Z~}8{}y~rn~}q{|y~} `{|Z~}a{}l~|X{|o~}|&{|" - "w~|m{}w~|]{}q~}w{|x~}W{|v~| xm~}V{|}q~|V{|v~|v{}w~}fm~}vw~o{|u~rm~}vw~|w{}n~|u{|m~|uw~|p{|u~3v~q{|v~\\v~q{|v~\\" - "v~q{|v~\\v~q{|v~\\v~q{|v~]{|v~pv~|e{}w~}r{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w" - "~|u{}v~tv~iv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|`{|p~}`v~}q{}x~}qv~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}" - "w~}Z{}v~}V{}w~}mv~}aw~}uu~}G{}w~L{}w~L{}w~L{}w~L{}w~L{}w~V{}w~}kw~}j{|v~O{|w~}kv~b{|w~}kv~b{|w~}kv~b{|w~}kv~Wv~" - "Lv~Lv~Lv~W{|v~l{}w~}`v~|n{|w~}b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|.{|v~r{|w~{}w~|a{|w~|m{}w~|a{|w~|m{}" - "w~|a{|w~|m{}w~|a{|w~|m{}w~|_{|w~}q{}w~|^v~}m{|w~}`{|w~}q{}w~| Ww~yd~|{w~jw~yd~|{w~jw~s{|r~|sw~jw~ou~|sw~jw~pv~}" - "qw~Zw~|U{|v~qv~| G{}w~}Uw~}sx~({}^~g{}Z~g]~}f{|_~|cw~}l{|w~|aw~}kw~}\\{|v~x{|v~Wv~t{}w~}cV~} 2v~| k{}w~| {{}" - "x~}t{}x~}Y{|}m~}`{|}w~}|tw~|v{|q~}j{}v~w{}u~p{}w~|,{|w~}F{}w~|Ox~Z{|Z~} t{}x~}X{|v~k{}w~}W{}w~}J{}v~|Ut|}t~}]{" - "|w~|r{}w~}K{}v~|a{|s~p{|v~}Tv~}W{}i~}]{}u~|t{|}s~} Z{|q~}| e{|}q~}\\v~}`x~}s{}w~ov~|t{}x~|k{|w~}p{}w~|`{}w~}p|}" - "t~|cv~}M{}w~}f{|v~|e{}w~}i|^{}w~}l|cv~}m{}n~|h{}w~}h|v~|Y{}w~}M{}w~}W{}w~}u~}Q{}w~}V{}w~|v{}w~w{|w~uv~|h{}w~|tv" - "~|uv~iv~}c{|v~|e{}f~|ev~}c{|v~|g{}i~}S{|}m~}V{|v~|Z{|v~h{}w~}a{}w~}rv~}ev~|v{|w~t{|w~uv~|`r~O{|v~|O{}v~}P{|w~}I" - "{}w~I{|w~| a{}w~^v~|lv~a{}w~}O{}w~|lv~|b{|w~|k{}w~Xw~}V{}w~|lv~|`v~m{|w~}Wv~Lv~Tw~}yu~|Mv~_v~mv~mv~hv~m{|w~}b{" - "}w~}l{}w~}`v~|m{|v~c{}w~|lv~|Zv~Q{}v~|Iv~S{|w~|m{}w~|_{|w~}q{}w~|cv~u{}x~}y{}x~}u{}w~^{}q~}Wv~qv~Q{|v~}Uy|}v~|K" - "w~|L{|u~}|^{|k~}|s{|}x~}5y~}q{}v~|q{}y~f{}w~}o{}u~|^v~ty|}s~[{|u~y}v~y|w~|a{|w~}l{}w~}^{}w~|Ov~m{|w~}]w~}rv~Wv~" - "|y{}w~}\\{|w~|m{}w~|gv~|b{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{|w~}b{}w~|m{|v~c{}w~}l{}w~}X{|w~}W`~`{|w~}pv~|" - "S{}v~|W{}w~}V{|v~bv~}lv~}r{|v~r{|v~|i{}w~}hv~|gu~t{|v~t{|v~}jv~}xh|y{|v~|mT~]{}w~]T~|^{}w~]{}U~|^{}w~ Hv~|ty~}X" - "{}g~|[w~|nw~|W{}u~}Mw~|V{}w~ov~1{|y~v{}x~}d{|y~|ay}x~y}ww|[{}w~}v{|v~|`{|Z~}8{|y~ro~o{|y~| Q{}w~R{}l~|V{|y}v~y}" - "|${|w~|m{}w~|\\{|}s~}w{|x~}W{|v~| xm~}T{|y}w~}|S{|v~|v{}w~}gm~}w{}x~}oy~y}x~rm~}w{}x~}v{}~}y|w{|v~u{|o~}t{}x~}o", - "t~^v|V{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{}w~}p{}w~}ev~|r{|v~h|lv~}I{}w~}i|_{}w~}i|_{}" - "w~}i|_{}w~}i|V{}w~}M{}w~}M{}w~}M{}w~}_v}u~r}nv~}h{}w~|tv~|uv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|" - "_{|r~}_v~}r{}w~q{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}mv~}aw~}u{|t~|I{}w~L{}w~L{}w~L{}w~" - "L{}w~L{}w~V{}w~|kv~j{}w~}O{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~Wv~Lv~Lv~Lv~W{}w~}l{|w~}`v~m{|w~}b{}w~}l{}" - "w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}eY|f{}w~}rw~y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|" - "m{}w~|^v~qv~]v~|m{|v~_v~qv~ Vw~yd~|{}x~|kw~yd~|{}x~|kw~r{|t~|r{}x~|kw~pu~|r{}x~|kw~pv~}q{}x~|[w~|T{}w~|s{|v~ G{" - "}v~T{}w~t{|y~}(]~|i{|Y~}h{|_~}d{|a~}bw~}kw~|aw~}kw~}\\{}w~}wv~|Xv~|u{}w~|cV~} 2v~| k{}w~| {{w~|tw~|W{|}m~}T{" - "}x~}v{|o~}l{|v~|v{}u~q{}w~+{|w~}F{}w~|Ox~Z{|Z~}+m| ww~|X{|v~k{}w~}W{}w~}K{}v~}K{|}v~}^w~}q{}w~}Ju~a{|t~|o{}v~U{" - "|v~|X{}u~}|wy|u~}]t~}y|{y|}q~} Z{|t~}| _{|}t~}\\v~`{|x~}s{}x~}o{|w~|t{}x~|kv~|p{|w~}`{}w~}n{|u~cv~}M{}w~}f{|v~|" - "e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|v{|w~|x{}x~}uv~|h{}w~|t{|v~uv~iv~}c{|v~|e{}h~" - "}cv~}c{|v~|g{}h~}Qy|y}p~W{|v~|Z{|v~h{}w~}a{|v~s{|v~|e{}w~}v{}x~}t{|w~uv~|a{}r~}P{|v~|P{}v~}O{|w~}I{|w~|J{|w~| " - "n{|y}l~^v~kv~a{}w~|Ov~|l{}w~|b{}w~|k{}w~|Yw~}Vv~|l{}w~|`v~m{|w~}Wv~Lv~Tw~}|u~Kv~_v~mv~mv~hv~m{|w~}b{}w~|l{|v~`v" - "~kv~c{}w~|l{}w~|Zv~Pu~}|Kv~S{|w~|m{}w~|^v~qv~b{}w~u{}x~|y{|w~uv~]{}r~V{}w~|s{|w~}R{|v~}X{|q~}Jw~|K{|q~}c{}g~}w|" - "}u~}5y~}pw~}p{}y~fv~|o{}u~]v~p{|t~\\v~}w{|w~}w~|a{}w~|l{|w~}]{}w~}y|Rv~m{|w~}]{}w~s{}w~}X{}w~}x{|v~\\{|w~|m{}w~" - "|h{|v~|b{|v~|i{}w~|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~kv~c{}w~|l{|w~}X{|w~}Wv~jv~`v~|p{}w~}T{}v~|V{}w~}V{|v~" - "|cv~|lv~}r{|v~r{|v~|i{}w~}hv~|g{}v~}u{|v~tu~|jv~}c{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ I{|v~sy~}X{}g~|[w~m{}x~|Vu~" - "|#{|w~|p{|w~|2{|y~|w{|x~}d{|y~|3v~}v{}v~|Aw~}8{|y~|sw~x{|w~}p{|y~| Q{}w~ p{|w~|m{}w~|Y{|}v~}w{|x~}W{|v~| jv~}" - "v{}v~|W{|w~o{}y~{}x~r{}n~}x{|w~uy|rw~|ty|t}|s{|w~o{}y~|}x~^{}w~|Wv~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|" - "w~}^v~|p{|v~f{|v~q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|t{|v~uv~iv~}c{|v~|lv~}" - "c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|^{|t~}^v~}s{}w~p{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w" - "~}n{|v~|aw~}t{}t~}W{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~c{|y}l~j{}w~j{}w~|O{}w~|k{}w~|c{}w~|k{}w~|c{}w~|k{}" - "w~|c{}w~|k{}w~|Xv~Lv~Lv~Lv~W{}w~|l{|v~`v~m{|w~}b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~f{|Z~}f{}" - "w~|s{}x~|y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|^{}w~|s{|w~}]v~kv~_{}w~|s{|w~} Vw~yd~|{}x~|kw~yd" - "~|{}x~|kw~qt~|r{}x~|kw~qu~|q{}x~|kw~pv~}q{}x~|[w~|T{|w~}s{}w~} H{|v~|T{|w~|u{}y~({|]~}i{}X~g{|`~b{}b~aw~}kw~}aw" - "~}kw~}\\v~|w{}w~}X{}w~}uv~bw~}Z| 5x|v~}p| v{}w~| {|w~t{|w~S{|}n~|Vw~uv~|y{|}w~}m{}w~}t{}u~rw~}+{|w~}F{}w~|Ox" - "~Z{|Z~},{|m~ x{|w~|X{|v~k{}w~}W{}w~}L{}v~}H{}v~}`{}w~p{}w~}J{}v~`t~n{|v~|V{}v~X{}v~}q{}v~}^{|j~|v~| Z{|t~| ]{|}" - "u~}]{|w~}`{|x~|sw~|o{|w~|t{}x~|l{|v~nv~`{}w~}lv~}dv~}M{}w~}f{|v~|e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{" - "}w~}{|t~S{}w~}V{}w~|u{}x~}y{|w~|uv~|h{}w~|sv~|vv~iv~}c{|v~|e{}k~}|av~}c{|v~|g{}w~}t|y}u~}M{|}s~}X{|v~|Z{|v~h{}w" - "~}`v~}t{}v~d{}w~}vw~|sw~|w{|v~a{|v~}v~|Q{|v~|Q{|u~N{|w~}Hw~|J{|w~| p{}h~|_v~k{}w~|bv~|Ov~k{}w~|bv~j}v~|Yw~}Vv~" - "k{}w~|`w~}m{|w~}Wv~Lv~Tq~}Jv~_w~}mv~mv~hw~}m{|w~}bv~|l{|v~`v~kv~|dv~k{}w~|Zv~P{}r~}y|Pv~S{|w~|m{}w~|^{}w~|s{|w~" - "}b{|w~|vw~|xw~|w{|w~}\\s~|Uv~sv~|Ru~W{|s~}|Iw~|I{|}t~}d{|u~}w|}g~}5{|y~|p{|x~|p{}y~fv~|o{|v~}]v~n{}v~|^{}w~|ts~" - "`v~|l{|v~\\{}p~}Xw~}m{|w~}]{|w~|tv~|Xv~|wv~|]{|w~|m{}w~|h{|v~|q{}x~}q{|v~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{" - "|w~}bv~kv~|dv~|l{|v~X{|w~}Wv~|l{|v~a{|v~nv~U{|v~}U{}w~}Uv~}d{|v~|l{}v~r{|v~r{|v~|i{}w~}hv~|fu~|v{|v~u{}v~}iv~}c" - "{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ rw|V{|w~}sy~}X{|w}u~q}Zw~m{}x~|V{}v~\"{|v~ow~|2{|y~|w{|w~d{|y~|4{}w~}v{|v~?w~" - "}8{|y~|sw~vw~}q{|y~| Q{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| i{}w~|v{|v~Ww~|p{|y~|{}x~`{}x~|j{|x~}bw~|p{|y~}{}x~^{" - "}w~|X{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|w~}nv~|g{}w~}q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}" - "M{}w~}Z{|v~ev~}h{}w~|sv~|vv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|]{}u~|^v~}t{|w~|p{|v~|i{|v~h{}w~}" - "f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}n{}v~|aw~}s{|s~|[{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|f{}h~j}v~" - "jv~|Ov~j}v~|cv~j}v~|cv~j}v~|cv~j}v~|Xv~Lv~Lv~Lv~Wv~|l{|v~`w~}m{|w~}bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v" - "~f{|Z~}fv~|t{}x~|wv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]v~sv~|]v~kv~|_v~sv~| Vw~yd~|{w~jw~yd~|{w~j" - "w~rr~|sw~jw~ru~|pw~jw~pv~}qw~Zw~|Sv~sv~ H{|v~|Rw~}uy~}({|]~}i{}X~|g{}b~|a{}d~|aw~}kw~}aw~}kw~}]{|v~v{|v~X{|v~v{" - "|w~}b{}x~} pf~ v{|w~ {{|w~t{|x~}P{|y~}r~W{}x~|v{}w~u{}w~mv~r{}u~t{|w~|+{|v~F{}w~|Ox~Z{|Z~},{|m~ x{}w~W{|v~k" - "{}w~}W{}w~}M{}v~}F{}v~a{|w~|p{}w~}Iv~|au~}mv~}Vv~|Y{|v~}o{|v~|]{}m~|{v~| Z{|r~}| c{|}r~}]{|w~}`{|x~|sw~|nw~|t{}" - "x~k{}w~}n{}w~}a{}w~}l{|v~|e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~mr|v~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|t~T{}w~}V{}w~|u" - "{|w~y{}w~tv~|h{}w~|s{|v~vv~i{}v~c{|v~|e{}w~}r|]{}v~c{|v~|g{}w~}q{}v~}K{|t~|Y{|v~|Z{|v~h{}w~}`{}v~tv~|d{|v~w{|w~" - "|s{}x~}w{}w~}av~}{}v~Q{|v~|R{|u~M{|w~}H{}x~}J{|w~| r{|f~|_w~}k{}w~|bv~|Ov~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~" - "Lv~Tq~Iv~_w~}mv~mv~hw~}m{|w~}bv~jv~`v~k{}w~|dv~k{}w~|Zw~}O{}o~}|Sv~S{|w~|m{}w~|^{|w~}s{}w~|b{|w~}w{|w~w{}x~}w{|" - "w~|\\{|u~}T{}w~|u{|w~}Ru~V{|s~}|Iw~|J{|}s~}d{|w~|s{|}k~|3y~}p{|x~}p{}y~fv~mv~|]v~m{}v~_{|w~}rt~`v~jv~Z{}r~}Xw~}" - "m{|w~}\\w~}u{|w~}X{|w~}v{}w~}]{|w~|m{}w~|h{|v~|q{}x~}pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~jv" - "~X{|w~}W{}w~|l{|v~a{}w~}n{}w~}W{|u~T{}w~}U{}w~}d{}v~k{}v~|s{|v~r{}v~h{}w~}hv~|f{|u~|w{|v~v{}u~h{}v~c{|v~|n{|T~]" - "{}w~]T~|^{}w~]{}U~}^{}w~ s{|w~V{|w~}sy~}S{|v~Pw~|nw~|V{|w~}!{}v~|q{}x~|1y~}vw~|e{}y~ci|]{}w~u{|w~|?w~}7y~}sw~v{" - "|w~|r{}y~ P{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| Fi|U{|w~|u{}w~X{}x~}p{|y~}y{}x~a{|w~i{|x~}c{}x~}p{|y~}y{}x~^{}w~|" - "X{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~|n{}w~}h{|v~p{|v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}" - "D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|s{|v~vv~i{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|^{}s~|_" - "{}v~u{|w~|o{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}o{|u~`w~}q{}t~|^{|f~|^{|f~|^{|f~|^{|f~|" - "^{|f~|^{|f~|h{|P~jv~|O`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~|u{}x~}" - "vv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]{}w~|u{|w~}\\v~k{}w~|_{}w~|u{|w~} Uw~yq}w~r}yw~jw~yd|yw~jw~" - "sp~|tw~jw~su~|ow~jw~pv~}qw~Zw~|S{}w~}u{|w~} Hv~|Q{}w~|w{|y~|({|\\~iW~|f{}d~|_e~|`w~}kw~}aw~}kw~|]{}w~}uv~Wv~|w{" - "}w~|b{}x~} q{|g~| v{|w~({}Z~X{|y~|{|}u~}Y{|w~uw~|tw~}o{|w~}q{}u~u{}w~*{|v~F{}w~|*m|}w~l|,{|m~ xw~}W{|v~k{}w" - "~}W{}w~}N{}v~}Dv~|bw~}o{}w~}Iv~|au~|m{}w~}W{|v~X{}v~m{}v~\\{|p~}xv~| Y{}p~}| i{|}p~}|]{}w~}`{|x~|sw~mw~|t{}x~kv" - "~}n|}v~a{}w~}kv~}e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~dv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}x{|t~U{}w~}V{}w~|tw~|{w~}tv~|" - "h{}w~|rv~}wv~i{}v~c{}v~d{}w~}T{}v~c{}v~f{}w~}p{}v~|Ju~}Y{|v~|Z{|v~h{}w~}_v~|v{|v~bv~|x{|w~r{}w~wv~|b{}v~xv~}R{|" - "v~|Ru~|M{|w~}H{|w~J{|w~| s{|q~t}v~|_w~}k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tp~Jv~_w~}mv~mv~hw~}" - "m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}N{|m~|Uv~S{|w~|m{}w~|]v~t{|v~`v~w{}x~}w{|x~}w{}w~[{|u~|T{|w~}u{}w~|S{}v~|V{|" - "x}t~}Jw~|K{|s~y}|d{|y~}n{|}p~}1y~}p{}w~p{}y~fv~mv~\\v~lv~|`{}w~|r{|v~}`v~jv~\\{|p~}Xw~}m{|w~}\\{}w~u{}w~|Xv~|v{" - "|v~]{|w~|m{}w~|h{|v~p{}w~pv~}iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~|l{|w~}av~|n{|v" - "~Wu~|T{}w~}U{}v~dv~}k{|v~}s{|v~s{|v~}h{}w~}hv~|e{}u~|x{|v~w{}u~|h{}v~c{}v~l{|u~}\\|]{}w~][|u~|]{}w~\\{}v~}c|u~}" - "]{}w~ s{|w~V{|w~}sy~}S{|v~P{}x~}o{|w~`{|a~}+u~|rw~|1y~}v{}w~ex~d{|j~}]{}w~}v{|v~|@w~}7y~}sw~u{}w~rx~ P{}w~ p{|" - "w~|m{}w~|Ux~}w{|x~} w{|j~}V{|v~|v{}w~}Xw~oy~}x{}x~aw~|i{|x~|cw~ox~x{}x~^{}w~|Xv~}n|}v~`v~}n|}v~`v~}n|}v~`v~}n|" - "}v~`v~}n|}v~a{|b~h{}v~p|}v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|rv~}wv~i{}v~c{" - "}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~^{}q~|`{}v~v{|w~}n{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{" - "|v~|V{}w~}p{|u~|`w~}p{|t~}`{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|i{|q~t}`~|kv~N`~|c`~|c`~|" - "c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~u{|x~}uv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m" - "{}w~|a{|w~|m{}w~|]{|w~}u{}w~|\\w~}k{}w~|_{|w~}u{}w~| U{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|uu~|u~|v{|w~j{}x~|uu~|o{|" - "w~j{}x~|qv}|r{|w~[{|w~|S{|v~uv~| TZ~}a{|w~}wx~'{|\\~iW~|ee~|^{|g~}_w~}kw~}aw~}kw~|]v~|u{}w~|X{}w~}wv~|b{|w~| " - " r{}g~ u{|w~({}Z~X{|y~|w{}v~|Zw~|v{|w~s{|w~o{|w~}p{}u~vw~})v~Fv~| w{}w~ x{|m~ y{|w~|Vv~|lv~|W{}w~}O{}v~}C{}w~}" - "c{|w~n|}w~}v|N{}w~}au~l{|v~Wv~}Xv~}m{|v~|[{|y}w~y}|x{|v~ V{|}p~}|XY|X{|}q~}|Z{}w~}`{|x~|sw~mw~|tw~l{|b~|b{}w~}k" - "{}v~e{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}w{|t~V{}w~}V{}w~|t{}w~|w~|tv~|h{}w~|r{|v~" - "wv~i{|v~|d{}v~d{}w~}T{|v~|d{}v~f{}w~}o{}v~J{|u~Y{|v~|Z{|v~h{}w~}_{}w~}v{}w~}b{}w~}x{}x~}r{|w~wv~b{|v~|x{|v~|S{|" - "v~|S{}v~|L{|w~}Gw~|K{|w~| t{|u~}|q{}w~|_v~k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}|u~Kv~_w~}mv~" - "mv~hw~}m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}L{|}o~}Vv~S{|w~|m{}w~|]{}w~}u{}w~}`{}w~|xw~|w{|w~wv~\\{|s~Sv~uv~S{}v~" - "|O{}v~}Kw~|L{|v~}|_{|~|j{|y}x~y}|/x~q{|v~}qx~fv~m{}x~}\\v~l{}w~|`v~pv~}`v~jv~]n~}Xw~}m{|w~}\\{|w~|vv~X{|v~t{}w~" - "|^{|w~|m{}w~|h{|v~p{}w~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~}l{}w~}b{|v~lv~|Y" - "{}v~|S{}w~}U{|v~}f{|v~|ju~|t{|v~s{}v~|h{}w~}hv~|dt~}y{|v~y{|t~|g{|v~|d{}v~k{|u~|?{}w~>u~|b{|v{}w~[{}v~|e{}v~}\\" - "{}w~ s{|w~V{|w~}sy~}S{|v~P{|w~o{}x~}`{|a~}+{|u~}|u{|w~0{}y~v{|w~}g{|y~}d{|j~}\\{}v~|w{|v~}Aw~}7{}y~sw~tw~}t{|y~" - "} P{}w~ p{|w~|m{}w~|Ux~}w{|x~} w{|j~}W{|v~|vv~}X{}x~|p{}y~|x{}x~b{}x~}hw~c{}x~}p{}y~|x{}x~^v~X{|b~|b{|b~|b{|b" - "~|b{|b~|b{|b~|b{}b~}id~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|r{|v~wv~i{|v~|d{}v" - "~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~_{}v~}u~|a{|v~|ww~}m{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" - "~}Z{|v~|V{}w~}sy|s~_w~}n{}u~|b{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|j{|u" - "~}|q{}a~|kv~N`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~.v~v{|w~tv~a{|w~|m{}w~|a{" - "|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|\\v~uv~[w~}k{}w~|^v~uv~ T{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|{|u~|w{|x~}j{}" - "x~}vu~|n{|x~}j{}x~}b{|x~}[{|w~Qv~|w{|v~ SZ~}`v~x{|y~}'{|]~}iW~|e{|g~}\\{}i~}^w~}kw~}aw~}l{|w~|^{|v~t{|w~}X{|v~x" - "{|v~`w~} m{|v~ jw|({}Z~X{|y~|v{}w~}[{}x~}u{}x~}s{|w~o{}w~}o{}u~x{|w~|)v~Fv~ v{}w~ g{}w~Uv~|lv~|W{}w~}P{}v~" - "}B{|v~c{|_~|O{}w~}a{}v~l{|v~X{|v~|Y{|v~|lv~|N{|v~ S{|}p~|[{|Z~}[{|}p~}|X{}w~}`{|x~|sw~|nw~|u{|x~}l{}b~}b{}w~}k{" - "|v~e{|v~}N{}w~}g{|v~}d{}w~}L{}w~}T{|v~}ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}v{|t~W{}w~}V{}w~|t{|r~sv~|h{}w~|q{}w~}xv" - "~i{|v~}dv~}d{}w~}T{|v~}dv~}f{}w~}nv~}J{}v~Y{|v~|Z{|v~|i{}w~}_{|v~vv~|b{}w~}xw~|qw~|y{|v~bv~}v{}v~S{|v~|T{}v~}K{" - "|w~}G{}x~}K{|w~| tv~}n{}w~|_v~kv~|bv~|Ov~k{}w~|bv~Bw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}{|u~|Mv~_w~}mv~mv~hw~}m{|w~" - "}bv~|kv~`v~k{}w~|dv~k{}w~|Zw~}Iy|}q~Wv~S{|w~|m{}w~|]{|v~uv~_{|w~|xw~uw~|y{|w~}\\r~}T{|w~|w{}w~}T{}v~|M{|v~Kw~|L" - "{}w~} O{}y~|rt~|s{|y~}fv~|nw~}\\v~l{|w~}`w~}p{}w~|`v~|kv~^u~}|Qw~}m{|w~}[w~}w{}w~}X{}w~|t{|w~}^{|w~|m{}w~|h{|v~" - "pv~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~|l{|v~X{|w~}W{|w~}l{}w~|b{}w~}l{}w~}Z{|v~}R{}w~}T{}v" - "~f{}v~i{|u~t{|v~t{|u~g{}w~}hv~|cr~}v~}s~}f{|v~}dv~}j{|u~|@{}w~?u~|b{}~|w{}w~vy~a{}v~|g{}v~}b{}~|w{}w~vy} {{}w~|" - "W{|w~}sy~}S{|v~Ow~}q{|w~|`{|a~}){}u~}vw~}0{|y~}v{}w~}p{|t{}y~|d{|j~}[{|v~|vv~}Bw~}7{|y~}tw~t{|w~|u{}y~| P{}w~ " - "p{|w~|m{}w~|Ux~}w{|x~} w{|j~}X{}v~v{|v~}X{|w~p{|y~|w{}x~bw~h{}x~|d{|w~p{|y~}w{}x~^v~X{}b~}b{}b~}b{}b~}b{}b~}b{" - "}b~}b`~j{}d~Y{|v~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fv~}g{}w~|q{}w~}xv~i{|v~}dv~}k{|v~}dv~}k" - "{|v~}dv~}k{|v~}dv~}k{|v~}dv~}`{}v~|{|u~|b{|v~}x{}x~}lv~}h{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}Z{|v~|V" - "{}e~|_w~}m{|u~bv~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|jv~}n{}w~Tv~|Ov~Lv~Lv~Lv~Av~Lv~Lv~Lv~" - "Wv~|l{|v~`w~}m{|w~}bv~|kv~bv~|kv~bv~|kv~bv~|kv~bv~|kv~.v~vw~|u{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}" - "w~|\\{|w~|w{}w~}[v~k{}w~|^{|w~|w{}w~} T{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~|x{|u~|x{}x~|j{|w~wu~|m{}x~|j{|w~b{}x~" - "|[{|w~Q{|w~}w{}w~} SZ~}`{}w~|y{}y~|'{|n~y}~|n~}i{}k~x}k~c{|i~}Z{}j~]w~}kw~}a{}w~l{|w~|^{}w~}sv~Wv~|y{}w~}`{}w~|" - " mv~| o{}Z~X{|y~|v{|w~}\\{|w~t{}x~|rw~|p{}w~}n{}u~yw~}(v~|Gv~ v{}w~ gw~}U{}w~}m{|v~V{}w~}Q{}v~}A{|v~c{|_~" - "|O{}w~}a{}v~l{|v~X{}v~X{|v~k{}w~}N{}w~} Q{|}p~}|^{|Z~}^{|}p~}|U{}w~}`{|x~}sw~|o{|w~|u{}x~|l`~b{}w~}k{|v~|eu~N{}" - "w~}g{}v~|d{}w~}L{}w~}Su~ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}u{|t~X{}w~}V{}w~|ss~}sv~|h{}w~|q{|v~|yv~hu~e{|v~|d{}w~}" - "Su~e{|v~|f{}w~}n{}v~|K{|v~|Z{|v~|Yv~|i{}w~}^v~|x{}v~a{|v~y{|w~|q{}x~}y{}w~}c{}v~tv~}T{|v~|U{|v~}J{|w~}G{|w~K{|w" - "~| u{|v~m{}w~|_v~kv~a{}w~|O{}w~|l{}w~|bv~|Cw~}V{}w~|l{}w~|`w~}m{|w~}Wv~Lv~Tw~}y{|u~|Nv~_w~}mv~mv~hw~}m{|w~}bv~" - "|l{|v~`v~kv~cv~|l{}w~|Zw~}D{|}u~}Xv~S{|w~|m{}w~|\\{}w~|w{|w~}^w~}y{|w~u{}x~}y{}w~|]{}q~|Tv~wv~|U{|v~}K{}w~|Lw~|" - "Lv~ N{|x~s{}x~{w~|tx~|fv~|o{|v~\\v~l{|w~}a{|w~|p{}w~_{}w~|l{|v~_{}v~|Ow~}m{|w~}[{}w~|xv~X{|v~rv~|_{|w~|m{}w~|h{" - "|v~|qv~pv~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~|bv~kv~c{}w~|l{|v~X{|w~}Vv~l{}w~|bv~|l{|v~[{|v~}Q{}w~}T{|v~}" - "h{|v~|hu~}u{|v~u{|u~|g{}w~}hv~|b{}f~|du~e{|v~|i{|u~|A{}w~@u~|b{}x~|x{}w~ww~a{}v~|i{}v~}b{}x~|x{}w~w{}y~} {}w~|W" - "{|v~sy~}S{|v~O{|w~}s{}w~}^q|}v~q|'{}t~|{|w~}.x~u{}v~}|wy|}y~tx~/{|v~|v{}w~}Cw~}6x~tw~s{}w~ux~ O{}w~ p{|w~|m{}w" - "~|Ux~}w{|x~} B{}w~}v{|v~|Ww~|q{|y~}v{}x~c{}x~|i{}x~}cw~|q{|y~}v{}x~_{|v~X`~b`~b`~b`~b`~c{|`~|kc~Xu~J{}w~}M{}w~" - "}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~g{|v~}g{}w~|q{|v~|yv~hu~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|a{}" - "v~|x{|u~|bu~y{}w~l{|v~|gv~|i{}w~}ev~|i{}w~}ev~|i{}w~}ev~|i{}w~}Z{|v~|V{}f~|^w~}l{|v~|d{|v~m{}w~|a{|v~m{}w~|a{|v" - "~m{}w~|a{|v~m{}w~|a{|v~m{}w~|a{|v~m{}w~|k{|v~m{}w~T{}w~|Ov~|Mv~|Mv~|Mv~|Bv~Lv~Lv~Lv~W{}w~|l{|v~`w~}m{|w~}bv~|l{" - "|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~.v~|x{}x~|t{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|[v~wv~|[v" - "~kv~\\v~wv~| Sw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|yu~|m{|w~hw~|d{|w~Z{|w~Pv~wv~| SZ~}_w~}yx~%n~{|~{|o~|" - "i{|l~}|}|l~}b{}j~Xk~|]w~}kw~}a{}w~l{}w~]v~|s{}w~|X{}w~}yv~|_w~} mv~} g{}x~}t{}x~}O{|y~|uw~}\\{}x~|t{}x~|rw" - "~|p{|w~}m{}u~}w~|({}w~|H{|w~} v{}w~ h{|w~|U{}w~}m{|v~V{}w~}R{}v~}@{|v~c{|_~|Ov~|a{|v~l{}w~}Xv~}X{|v~k{}w~}Nv~|" - " N{|}p~}|a{|Z~}a{|}p~}|R{|w}|_x~}s{}x~}o{}w~|v{|w~l{}`~|c{}w~}k{|v~|e{}v~|O{}w~}gu~c{}w~}L{}w~}S{}v~|fv~|h{}w~}" - "hv~|Y{}w~}M{}w~}W{}w~}t{|t~Y{}w~}V{}w~|s{}t~rv~|h{}w~|p{}w~}yv~h{}v~|f{}v~c{}w~}S{}v~|f{}v~e{}w~}mv~}K{|v~|Z{|v" - "~|Yv~|iv~|^{}w~}xv~}`v~|{|w~p{}w~yv~|d{|v~|t{|v~|U{|v~|V{|u~I{|w~}Fw~|L{|w~| u{}w~|mv~|_v~|m{|v~a{}w~}O{}w~|lv" - "~|b{}w~|Cw~}V{}w~|lv~|`w~}m{|w~}Wv~Lv~Tw~}x{|u~|Ov~_w~}mv~mv~hw~}m{|w~}b{}w~|l{|w~}`v~kv~c{}w~|lv~|Zw~}B{|u~Xv~" - "S{|w~|m{}w~|\\{|w~}w{}w~|^v~y{}x~}u{|x~}y{}w~]{|v~|}v~T{}w~|y{|w~}U{|v~}J{|w~}Lw~|M{|w~} Mx~}v{|w~|{|w~|v{}x~e{" - "}w~|o{}v~\\v~l{|w~}a{|w~|pw~}_{}w~|l{|w~}_v~Mw~}m{|w~}[{|w~}y{|w~}X{}w~|r{}w~}_{|w~|m{}w~|h{|v~|qv~|r{|v~|i{}w~" - "|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{}w~|bv~kv~c{}w~|l{|w~}X{|w~}Vv~lv~b{}v~jv~|\\u~P{}w~}S{}v~|iu~g{|t~|w{|v~v{}u~}" - "f{}w~}hv~|a{}h~|c{}v~|f{}v~g{|u~|B{}w~Au~|b{}v~|y{}w~xu~a{}v~|k{}v~}b{}v~|y{}w~x{}w~}!{}w~|Vv~sy~}S{|v~O{|u~}y|" - "{y|u~}T{|w~}Lw}|P{|}p~}-{|y~}u{}l~u{}y~|.{|v~|v{}w~}Dw~}6{|y~}uw~rw~}w{}y~| O{}w~ p{|w~|m{}w~|Ux~}w{|x~} C{}w" - "~}v{|v~|W{}x~}px~u{}x~d{|w~i{}x~}c{}x~}px~u{}x~_{}w~}Y{}`~|d{}`~|d{}`~|d{}`~|d{}`~|d{}w~}j|}w~}l{|c~X{}v~|K{}w~" - "}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|p{}w~}yv~h{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~" - "i{}v~|f{}v~a{}v~|v{|u~|c{}v~|}w~|l{}v~fv~|iv~|ev~|iv~|ev~|iv~|ev~|iv~|Z{|v~|V{}h~}\\w~}k{}w~|d{}w~|mv~|a{}w~|mv" - "~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|k{}w~|mv~|U{}w~}O{}w~|M{}w~|M{}w~|M{}w~|Bv~Lv~Lv~Lv~W{}w~}l{}w~}`w~}m" - "{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}.{}w~|y{}x~|s{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w" - "~|m{}w~|a{|w~|m{}w~|[{}w~|y{|w~}Zv~kv~\\{}w~|y{|w~} R{|w~r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~y{|w~|lw~}" - "h{|w~dw~}Z{|w~P{}w~|y{|w~} Rs}v~g}|_{}w~{|y~}%{|p~|{|~yp~}g{}m~{}~{}m~|a{}l~|X{|m~}\\w~}kw~}a{|w~|mv~]v~r{}w~}X" - "{|v~{|v~^{}w~} n{}v~ gw~|tw~|O{|y~|uw~}]{|x~}sw~|rw~|p{|v~l{}r~}'{|w~}H{|w~} v{}w~ h{|w~T{|v~m{}w~}V{}w~}" - "S{}v~}?{|v~c{|_~|Ov~|`v~|m{}w~}Y{|v~W{|v~k{}w~}O{|v~ J{|}p~}|d{|Z~}d{|}p~}|-w~s{|w~ov~|v{}x~|lv~|j{|v~c{}w~}k{}" - "v~cv~}O{}w~}h{}v~|c{}w~}L{}w~}Rv~}fv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}rt~Z{}w~}V{}w~|ru~}rv~|h{}w~|p{|v~|{v~h{|v~}g" - "{|v~}c{}w~}Rv~}g{|v~}e{}w~}m{|v~|L{|v~|Z{|v~|Y{}w~}j{|v~|^{|v~|{|v~_{}w~}{}x~}p{|w~yv~cv~}r{}v~U{|v~|W{|u~|I{|w" - "~}F{}x~}L{|w~| u{}w~|n{|v~|_v~}m{}w~}a{|w~}O{|w~}m{|v~|b{}w~}Cw~}V{|w~}m{|v~|`w~}m{|w~}Wv~Lv~Tw~}vu~|Pv~_w~}mv" - "~mv~hw~}m{|w~}b{|w~}l{}w~}`v~|m{|w~}c{|w~}lv~|Zw~}@v~|Yv~S{|w~}mv~|[v~wv~]{}w~|{w~|u{|w~yw~}]v~}y{}v~U{|w~}y{}w" - "~|V{|v~}I{|w~}Lw~|M{|w~} M{|w~x}v~}x{}v~x}w~|e{}w~}ou~|]v~l{|w~|a{|w~p{}w~|_{|w~}l{}w~}`{|w~}Mw~}m{|w~}Zv~y{}w~" - "|Y{|v~q{|v~_{|w~}m{}w~|gv~|r{|v~|r{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{}w~|bv~|m{|w~}c{|w~}l{}w~}X{|w~}V{}w~" - "|n{|w~}bv~}j{}v~]{}v~|P{}w~}Ru~j{}v~|f{|t~}|y{|v~x{|t~}e{}w~}hv~|`{|}l~}`v~}g{|v~}f{|u~|C{}w~Bu~|`u~|{}w~yu~|`{" - "}v~|m{}v~}a{|u~|{}w~y{}v~}!{}w~|Vv~|ty~}S{|v~P{|g~}U{|w~}Lw~|N{|r~}+{}y~|u{|}o~}v{|y~}+v~}v{}v~Ew~}5{}y~|vw~r{|" - "w~|y{|y~} N{}w~ p{|w~}m{}w~|Ux~}w{|x~} Dv~}v{}v~|W{|w~p{}y~|u{}x~dw~|j{}w~c{|w~p{}y~|u{}x~`{}v~|Yv~|j{|v~dv~|" - "j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~l{}w~}n{|v~Wv~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{" - "|v~}f{}w~|p{|v~|{v~h{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}b{}v~|t{|u~|cq~|l{|v~}f{}w~}j{|v" - "~|e{}w~}j{|v~|e{}w~}j{|v~|e{}w~}j{|v~|Z{|v~|V{}k~}|Zw~}k{}w~}d{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{" - "}w~|n{|v~|a{}w~|n{|v~|k{}w~|n{|v~}U{|w~}O{}w~}M{}w~}M{}w~}M{}w~}Bv~Lv~Lv~Lv~W{|v~lv~|`w~}m{|w~}b{|w~}l{}w~}b{|w" - "~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}Xt|X{}w~}{}x~}r{}w~}a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|[{|w~}y" - "{}w~|Zv~|m{|w~}\\{|w~}y{}w~| Qw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}y{|y~|l{}w~fw~}f{}w~Y{|w~P{|v~y{}w" - "~| Kv~}J{|w~|}y~|${}r~}y{}~y{|q~f{|n~|{}~yn~}_m~|V{|o~}[w~}kw~}`w~}n{|w~}^{|w~}r{|v~Wv~{}w~}]v~| o{|v~| hw" - "~t{|w~N{|y~|uw~}]w~|s{}x~|rw~|ov~|l{}s~&{|w~}H{}w~| v{}w~ h{}x~}Sv~|nv~|V{}w~}T{}v~}>{}w~}Q{}w~}J{}v~_{}w~}mv~" - "}Y{}w~}Vv~|lv~|Ov~} G{|}p~}|0{|}o~}*{}x~rw~}q{}v~|w{}w~l{|v~hv~|d{}w~}ku~c{}v~}P{}w~}i{}u~b{}w~}L{}w~}R{}v~|gv~" - "|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}qt~[{}w~}V{}w~|r{}v~|rv~|h{}w~|o{}w~}{v~g{}v~|hu~|c{}w~}R{}v~|hu~|e{}w~}lv~}L{}v~Y" - "{|v~|Y{}v~j{}v~\\{}w~}{}w~}_{|v~{w~|ow~y|v~d{}v~pv~}V{|v~|Wu~|H{|w~}F{|w~L{|w~| u{}w~m{}v~|_u~mv~|a{|v~Nv~|n{}" - "v~|b{|v~Cw~}Uv~|n{}v~|`w~}m{|w~}Wv~Lv~Tw~}uu~|Qv~_w~}mv~mv~hw~}m{|w~}b{|v~lv~|`v~}m{}w~}c{|v~m{|v~|Zw~}@{}w~|Yv" - "~S{|w~}mv~|[{}w~|y{|w~}]{|w~}{w~sw~y|w~}^{}w~}wv~}U{}w~yv~Uv~}Gw~}Lw~|M{|w~| L{|q~}v{}q~|d{|w~}p{|u~|]v~l{}w~|a" - "{|w~pv~^{|v~lv~|`{|w~|Mw~}m{|w~}Z{}w~y|v~X{}w~}pv~|`{|w~}mv~|gv~}r{|v~}r{}v~h{|v~u{}w~u{|w~}a{|w~}q{|w~}`{}w~|u" - "{}w~tv~av~}m{}w~}c{|v~lv~|X{|w~}V{|w~}n{}w~|c{|v~i{|v~|_{}v~}O{}w~}R{|v~}l{}v~|d{|r~y}v~y}s~}d{}w~}hv~|]{|}s~y}" - "|^{}v~|hu~|e{|v~}C{}w~C{}v~|^u~|}w~{}v~|^{}v~n{|v~}_{|u~|}w~{}v~} {}w~|V{}w~}ty~}S{|v~Q{}e~}V{|w~}Lw~|L{|t~*{|x" - "~|t{|y}u~}|u{|x~|*{}w~|v{|v~Fw~}5{|x~|ww|qw|y{|x~| >{|w~}mv~|Ux~}w{|x~} Ev~}v{|v~U{}x~|q{|y~}t{}x~e{}x~}j{}w" - "~b{}x~|q{|y~}t{}x~a{}v~}Y{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{}v~hv~|n{|v~|n{|v~W{}v~}L{}w~}M{}w~}M{}w" - "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~|o{}w~}{v~g{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|c{}" - "v~|r{|u~|d{}s~|ku~|f{}v~j{}v~d{}v~j{}v~d{}v~j{}v~d{}v~j{}v~Y{|v~|V{}w~}r|Vw~}k{|w~|d{}w~m{}v~|a{}w~m{}v~|a{}w~m" - "{}v~|a{}w~m{}v~|a{}w~m{}v~|a{}w~m{}v~|k{}w~m{}u~U{|v~O{|v~M{|v~M{|v~M{|v~Bv~Lv~Lv~Lv~Vv~|n{|v~_w~}m{|w~}b{|v~lv" - "~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|X{}u~X{|v~|x~}qv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|Z{}w~yv~Yv~}m{}" - "w~}[{}w~yv~ P{|w~}t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}yy|l{|w~}f{|w~}h{|w~}Y{|w~Ov~y|v~ K{}w~}Hw~}y" - "~}\"{}t~}x{}~x{|s~d{|p~}y{}~y{|p~}]o~|T{}p~Zw~}kw~}`{}w~|o{}w~|^{}w~|qv~|X{}w~|v~|]{|v~| o{}v~j{} {|x~}t{|" - "x~}N{|y~|v{}w~}^{}x~}r{}x~}rw~|o{}v~k{}u~|%v~Hv~ u{}w~ hw~|S{}v~o{}v~U{}w~}U{}v~}>{|v~}Q{}w~}Ju~_{|v~n{|v~|Z{|" - "v~|Vv~}m{|v~|P{}v~ C{}o~}4{|o~}|({|x~}s{}w~}s{}u~|x{}w~|l{}w~}h{}w~}d{}w~}l{|v~}bu~|g{|}g{}w~}j{}u~|b{}w~}L{}w~" - "}R{|u~|hv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}pt~\\{}w~}V{}w~|r{|v}qv~|h{}w~|nv~|v~g{|u~|j{}v~}b{}w~}R{|u~i{}v~}d{}w~}" - "l{|v~|M{}v~Y{|v~|Y{|v~|kv~}\\{|v~{v~|_{|v~|w~|o{}x~y}w~}e{|v~|p{|v~|W{|v~|X{}v~}G{|w~}F{|w~|M{|w~| u{}w~|nu~|_" - "u~}o{}v~_v~}O{}w~}o{|u~|av~}Dw~}U{}w~}o{|u~|`w~}m{|w~}Wv~Lv~Tw~}t{}v~|Rv~_w~}mv~mv~hw~}m{|w~}av~|n{|v~_u~mv~|bv" - "~|n{}v~|Zw~}@{}w~|Yv~Rv~n{}v~|[{|w~}y{}w~|\\w~}|x~}s{}x~y}w~|_{}v~v{|v~|V{|w~y}w~}Vu~Fw~}Lw~|M{|w~| K{|s~}t{}s~" - "|bv~p{}u~}]v~|mv~`{|w~q{}w~}]v~}n{}v~_{|w~|Mw~}m{|w~}Yw~y}w~|Xv~o{|w~}`{|v~n{|v~|g{}v~r{}u~rv~}gv~|v{}w~uv~|a{|" - "w~}q{|w~}`{|w~}u{}w~u{|v~au~mv~|bv~}n{}v~Vv~Uv~nv~b{}w~}hv~}`{|v~}N{}w~}Q{|v~}n{}v~}b{|c~}c{}w~}hv~|Z{|v~Z{|u~|" - "j{}v~}c{|w~B{}w~B{}x~|\\u~}w~}v~|\\{}x~|m{}x~}]{|u~}w~}v~} {{v~|V{|v~|uy~}S{|v~R{}v~y|q~}|u~W{|w~}Lw~|J{}u~*{|x" - "~|e{|x~|({}x~}u{|w~F{|x}|4{|x~|e{|x~| ={|v~n{|v~|Ux~}w{|x~} Ew~|u{|x~}U{|x~}p{}j~}iw~j{}w~b{|x~}p{}j~}f{}v~}" - "X{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}fv~}h{}w~}n{}w~}m{|v~Vu~|g{|}c{}w~}M{}w~}M{}w~}M{}w" - "~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~|nv~|v~g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}c{" - "}v~|p{|u~|e{|t~}k{}v~}e{|v~|kv~}d{|v~|kv~}d{|v~|kv~}d{|v~|kv~}Y{|v~|V{}w~}Mw~}k{}w~|d{}w~|nu~|a{}w~|nu~|a{}w~|n" - "u~|a{}w~|nu~|a{}w~|nu~|a{}w~|nu~|k{}w~|nt~|Uv~}Ov~}Mv~}Mv~}Mv~}Cv~Lv~Lv~Lv~V{}v~nv~}_w~}m{|w~}av~|n{|v~`v~|n{|v" - "~`v~|n{|v~`v~|n{|v~`v~|n{|v~W{}u~Wr~q{|v~_v~n{}v~|`v~n{}v~|`v~n{}v~|`v~n{}v~|Z{|w~y}w~}Yu~mv~|[{|w~y}w~} O{}w~}" - "u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}X{}w~O{|w~y}w~} L{}w~}G{}u~|!{|}x~}|w{}~v{}w~}b{|r~|" - "x{}~w{}s~|\\{|q~}Rq~|Zw~}kw~}`{|v~p{|v~]v~p{}w~}X{|q~[{}v~} p{|v~}ly}$v}|\"{}x~}t{}x~}Yy}|s{|y~|w{|v~|_{|w~" - "q{}x~}s{|w~n{|v~}l{|u~}%{}w~|Iw~} u{}w~L{}w~} tv}|P{|w~R{|v~|pv~}U{}w~}V{}v~}={}v~|Q{}w~}K{}v~|^v~|o{}v~Y{}v~U{" - "}v~m{}v~P{|v~}U{|v}M{}w~}F{|}q~}6{|q~}|G{|w}|^w~ru~y|x{|}t~y|}v~|kv~|h{|v~d{}w~}m{|u~|b{|u~|i{|~}g{}w~}l{|}u~}a" - "{}w~}L{}w~}Q{}u~|iv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}ot~]{}w~}V{}w~|bv~|h{}w~|n{}q~f{}u~k{}u~a{}w~}Q{}u~k{|u~c{}w~}" - "kv~}c{|}h{|v~}Y{|v~|X{}v~l{}v~|[v~}v~]v~}w~n{}r~|ev~}n{}v~W{|v~|Y{}v~}F{|w~}Ew~}M{|w~| u{}w~|o{}u~|_t~|q{|v~}_" - "{|v~|P{|v~}pt~|a{|v~|Ew~}U{|v~|pt~|`w~}m{|w~}Wv~Lv~Tw~}s{}v~|Sv~_w~}mv~mv~hw~}m{|w~}a{}v~nv~}_u~}o{}v~a{}v~o{|u" - "~|Zw~}@{}w~|Y{}w~|Sv~|p{|u~|Zv~{|v~[v~}x~}s{|r~_{|v~|u{}v~Uq~V{}v~|Fw~}Lw~|M{|w~| I{|y}~y}|r{|}x~}|`{}w~}qs~]u~" - "n{|v~`{|w~r{|v~\\{|v~nv~}_{|w~}Mw~}m{|w~}Y{}r~X{}w~}nv~`{|v~|o{}v~|g{|v~|st~|t{|v~|g{}v~v{}w~v{|v~`{|w~}q{|w~}_" - "v~|v{}w~uv~}au~}o{}v~a{|v~nv~}Vv~U{|w~}p{}w~}bv~|h{|v~`u~M{}w~}P{|u~|q{}v~}_{}g~}|b{}w~}hv~|Z{|v~Y{}u~k{}u~a{|y" - "~A{}w~A{}~|Zl~|Z{}~|k{}~}[{|l~} yv~}Uv~}uy~}S{|v~S{}v~|x{|y}x~}|wu~X{|w~}Lw~|I{|v~}*{}x~|g{|x~}&{}y~}t{|x~ T{}x" - "~|g{|x~} <{|v~|o{}v~|Ux~}w{|x~} Ex~|t{|y~}Tw~|p{}j~}j{}x~|k{}x~}aw~|p{}j~}g{}v~}Wv~|h{|v~fv~|h{|v~fv~|h{|v~f" - "v~|h{|v~fv~|h{|v~g{|v~g{|v~|ov~|m{|v~V{|u~|i{|~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}w~" - "|n{}q~f{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~c{}v~|n{|u~|e{}u~|l{}u~c{}v~l{}v~|c{}v~l{}v~|c{}v~l{}v~" - "|c{}v~l{}v~|Y{|v~|V{}w~}Mw~}kv~c{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|k{}w~|o{" - "}s~U{|v~|P{|v~|N{|v~|N{|v~|N{|v~|Dv~Lv~Lv~Lv~Uv~}p{}v~^w~}m{|w~}a{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}W{" - "}u~W{}t~|qv~}_v~|p{|u~|`v~|p{|u~|`v~|p{|u~|`v~|p{|u~|Yq~Xu~}o{}v~Yq~ M{}w~}|w{}x~}v{}v~b{}w~}|m{}v~b{}w~}|m{}v~" - "b{}w~}|m{}v~b{}w~}|m{}v~W{}x~}Nq~| M{|v~F{|u~ py~|V{|}y~y}|vy~|w{|y}y~y}Y{|s~}Q{|s~|Yw~}kw~}_{}v~|s{}v~|^{|w~}p" - "{|v~X{|r~}Z{}u~} q{}v~}o{|y~}$v~}\"w~|tw~|Y{}y~}|u{|y~|x{|u~^{}x~|q{|w~s{}x~}mu~}n{|s~}&{|w~|J{|w~| u{}w~L{" - "}v~ u{|v~|P{}x~}Q{}v~|r{}v~T{}w~}W{}v~}O{}|k{}v~}P{}w~}]{}|l{}u~]{|v~|q{}v~|Yv~}U{|v~}o{}v~}Q{|v~}T{}v~M{}v~C{|" - "}t~}6{|t~}|D{}w~}^{}x~|s{|m~y}q~|k{|v~fv~|e{}w~}n{|u~}`{}u~|l{|}y~}g{}w~}n{|}t~}`{}w~}L{}w~}P{}u~}jv~|h{}w~}hv~" - "|Y{}w~}M{}w~}W{}w~}nt~^{}w~}V{}w~|bv~|h{}w~|mq~e{}u~|n{}u~|a{}w~}P{}u~|n{}u~|c{}w~}k{|v~|d{|y~}k{|u~|Y{|v~|X{|u" - "~n{|u~Z{}r~}]{}s~}n{|r~e{}v~lv~}X{|v~|Z{|u~E{|w~}E{}w~M{|w~| u{|v~p{|t~|_s~|s{|u~]u~|P{}v~}s{|s~|`u~|k{|Ww~}T{" - "}v~}s{|s~|`w~}m{|w~}Wv~Lv~Tw~}r{}v~}Tv~_w~}mv~mv~hw~}m{|w~}`v~}p{}v~|_t~|q{|v~|`v~}q{|t~|Zw~}Q{|kv~|Y{}w~}S{}v~" - "pt~|Z{}w~y}w~}[{}s~|rs~}_v~}s{}w~}V{}s~}W{}v~|Ew~}Lw~|M{|w~| r{|v~|s{}s~}^u~}ov~|_w~|s{}w~|[v~}pu~]v~|Nw~}m{|w" - "~}Y{|s~}Xv~m{}w~|a{|u~p{|u~|fv~}t{}x~}x~}t{}v~ev~}w{}w~w{|v~}`{|w~}q{|w~}_{}v~|w{}w~v{}v~`t~|q{|v~|`v~}p{}v~U{}" - "w~|Uv~|r{|v~b{|v~fv~|bu~|M{}w~}O{|u~}t{|u~}\\{|k~}|`{}w~}hv~|Z{|v~X{}u~|n{}u~|`{|@{}w~@{|Xn~|X{|i{|Y{|n~} xv~}U" - "{|v~}vy~}S{|v~T{|v~|jv~}Y{|w~}Lw~|H{|v~|*{}x~}i{}x~}${}~}s{|y~ S{}x~}i{}x~} ;{|u~p{|u~|Ux~}w{|x~} Ey~|s{|~}T" - "{}x~}o{}j~}k{|w~k{}x~}a{}x~}o{}j~}h{}v~}W{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{}w~}f{}w~}p{|v~l{|v~U{}u" - "~|l{|}y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}w~|mq~e{}u~|n{}u~|d{}u~|n{}u~|d{}u~|n{}u" - "~|d{}u~|n{}u~|d{}u~|n{}u~|d{}v~|l{|u~|et~|n{}u~|c{|u~n{|u~b{|u~n{|u~b{|u~n{|u~b{|u~n{|u~X{|v~|V{}w~}Mw~}x{|p{}v" - "~c{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|k{|v~p{|q~j{|gu~|Pu~|k{|_u~|k{|_u~|k{|_u~|k{" - "|Vv~Lv~Lv~Lv~U{|v~}r{}v~}^w~}m{|w~}`v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|W{}u~Vu~|q{}v~|_{}v~pt~|`{" - "}v~pt~|`{}v~pt~|`{}v~pt~|Y{}s~}Xt~|q{|v~|Y{}s~} Lu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|W{}x~}N{}s~} " - "M{|v~|Ev~} py~|Jy~|M{}t~O{|u~}Xw~}kw~}_{|t~}w|}u~}]{}w~}ov~|Xr~|Y{}t~}y| tt~|r{}x~}$v~}\"w~t{|w~X{}v~}y|y{|" - "y~y|}t~|_{|x~}ow~}tw~|m{|t~|r{|}q~}&w~}J{}w~ t{}w~L{}v~ u{|v~|Pw~|Pu~|t{}v~|\\s|}w~}r|a{}v~}Nx~}|p{}t~O{}w~}]{}" - "y~}|q{}t~|\\{}v~|s{}u~Y{|v~|T{}u~|r{}u~|_{~}|r{|}u~|T{}v~M{}v~@{|}w~}6{|w~}|A{}w~}^{|w~r{|o~}{}s~}iv~}f{}w~}e{}" - "w~}q|y}s~|_{}t~|p{|}w~}g{}w~}r|y}q~}_{}w~}g|`{}w~}O{}t~}o{|}u~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}mt~_{}w~}h|i{}w~|bv~" - "|h{}w~|m{}r~dt~}|r{|t~|`{}w~}Ot~}q{|t~}b{}w~}jv~}d{|w~}|p{|}u~}X{|v~|W{}u~|q{}u~|Z{|r~|]{|s~|mr~f{|v~|l{|v~|Y{|" - "v~|[{|u~}b|^{|w~}E{|w~|N{|w~| tv~}r{}s~|_w~y}x~}|w{|}u~|]{|u~|p{|}|^t~y|x{|}w~}w~|`{|u~|n{|y~|Xw~}St~y|x{|}w~}" - "w~|`w~}m{|w~}Wv~Lv~Tw~}q{}v~}Uv~_w~}mv~mv~hw~}m{|w~}`{}v~}r{}v~}^s~}s{|v~}_{}v~}s{|s~|Zw~}Qy~}|o{}w~}X{|v~}|U{|" - "v~}s{|s~|Z{|q~Z{|s~qs~|`{}w~}qv~}Vs~|X{}v~|Dw~}Lw~|M{|w~| qu~|u{}w~|v~}_s~|s{|u~^{}w~t{}w~}Z{|v~}|t{|}v~|]{}v~" - "|ny|^w~}m{|w~}Xs~|Y{}w~}m{|w~}a{|t~|s{}t~}f{}v~}v{|w~{w~|v{}v~}e{}v~}x{}w~x{}u~_{|w~}q{|w~}^u~}|y{}w~x{}u~}`s~}" - "s{|v~}_{|v~}|t{|}v~|U{|v~}|W{|v~|t{|v~|bu~f|v~}c{}v~}h|_{}w~}Vs}t~}v{}t~s}`{|y}t~y}|]{}w~}hv~|Z{|v~Wt~}|r{|t~|#" - "{}w~ vp~| {|p~} wv~}T{}v~}wy~}v{}~Z{|v~S{}x~|hx~}X{|w~}Lw~|G{}w~}){|w~|m{|w~|\"{|}q{} R{|w~|m{|w~| XY| ${|u~}r{" - "|t~}Ux~}w{|x~} E{}qy|T{|w~c{}x~gw~|lw~}a{|w~c{}x~e{}v~}Vv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~|f" - "{|v~pv~}l{|v~}h|h{}t~|p{|}w~}c{}w~}g|a{}w~}g|a{}w~}g|a{}w~}g|X{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}w~|m{}r~dt~}" - "|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|d{|v~|j{|v~}f{}s~}|r{|t~|a{}u~|q{}u~|a{}u~|q{}u~|a{}u~|q{}u~" - "|a{}u~|q{}u~|X{|v~|V{}w~}Mw~}xy~}y|wy|u~|bv~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|jv~}r{}w~}" - "|u~|o{|}y~g{|u~|p{|}|_{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|Wv~Lv~Lv~Lv~T{}u~}|x{|}u~}]w~}m{|w~}`{}v~}" - "r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}V{}u~V{|v~}r{}v~}^{|v~}s{|s~|`{|v~}s{|s~|`{|v~}s{|s~|`{|v" - "~}s{|s~|Xs~|Xs~}s{|v~}Ws~| K{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~U{}x~}N{|s~| M" - "{|w~|D{}w~| q{|y~}K{|y~}L{}v~|N{}v~Ww~}kw~}^{|j~}\\v~|o{}w~}X{}s~W{|^~} -s~}v|}v~}$v~}#{|w~t{|x~}X{}e~|^w~|o" - "{|w~|v{}w~k{|s~}v|}t~y}v~}'{}w~Jw~} t{}w~L{}v~ u{|v~|Q{|w~O{|u~}w|}u~}\\{|e~|ab~`u~w}|x}r~|O{}w~}]{}v~w}|x}s~}Z" - "t~}w|}t~X{}w~}S{|t~y}v|}t~}^v~y}y|y}s~|S{}v~M{}v~={|}~}6{|y~}|?{}w~}]w~}q{}r~|y{}u~}h{|v~|f{|v~e{}c~|]{}s~y}v|y" - "}t~}g{}b~|^{}c~}`{}w~}N{}r~y}v|y}r~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}lt~`{}d~}i{}w~|bv~|h{}w~|lr~cr~}v|}s~}_{}w~}Ns~" - "y}v|}s~}a{}w~}j{|v~|e{|s~}u|}r~W{|v~|Vs~}v|y}t~|Xs~}\\{|s~|m{}t~}fv~}j{}v~Y{|v~|[{}\\~}^{|w~}Dw~}N{|w~| t{}u~y" - "|x{|}w~y}w~|_w~}{k~|[{|t~}|wy|}x~|^{|k~y|w~|_{|t~}|vy|y}w~|Xw~}S{|k~y|w~|`w~}m{|w~}Wv~Lv~Tw~}p{}v~}Vv~_w~}mv~mv" - "~hw~}m{|w~}_{}u~}|x{|}u~}]w~y}w~y|yy|}u~|^{}u~}|x{|}w~}w~|Zw~}Qv~}y|v{|}u~|Wm~[{}u~}w|}v~}w~|Y{}s~}Yt~}q{}t~|a{" - "}v~p{|v~|W{}t~W{}d~Uw~}Lw~|M{|w~| q{|u~}|y{|}v~{|s~br~}y|yy|}u~|^{|w~}v{}v~X{}u~}y|{y|}u~}\\{|t~}|vy|y}y~}^w~}" - "m{|w~}X{}t~Xv~|lv~|b{|s~}v|}q~x}hu~}|{y|w~}{}w~}|{|}u~c{}u~}|}w~|}t~|_{|w~}q{|v~}_{|s~}v~}s~}_w~y}w~y|yy|}u~|^{" - "}u~}y|{y|}u~}S{}r~}Z{}v~}|x{|}v~}b{|Z~c{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~Vr~}v|}s~|\"{}w~ ur~| y{|r~} vv~" - "}St~}y|y~}{y|}x~`{}b~}a{}~|f{~}W{|w~}Lw~|G{|w~}({|v~}|s{|}v~| E{|v~}|s{|}v~| X{|Z~} ${|s~}y|{y|}q~}|}Xx~}w{|x~" - "} l{}x~|c{}x~h{}x~}m{|w~|`{}x~|c{}x~f{|v~}V{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~dv~|r{|" - "v~k{|b~g{}s~y}v|y}t~}c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}w~|lr~cr~}v|}s~}`r~}v|}s~}`r~}v|}" - "s~}`r~}v|}s~}`r~}v|}s~}b{|x~|h{|x~}f{}o~}v|}s~}_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|W{|v~|V{}w~}Mw~}xk~}" - "a{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|j{}" - "u~y|x{|}u~y{|t~}|vy|}v~f{|t~}|wy|}x~|^{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|Wv~Lv~Lv~Lv~S{" - "}j~}\\w~}m{|w~}_{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}U{}u~V{}t~}|x{|}u~}\\{" - "}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|X{}t~Ww~y}w~y|yy|}u~|W{}t~ I{}h~}\\{}h~}\\{}h~}\\{}h~" - "}\\{}h~}T{}x~}Ms~ K{|y~}C{|w~ p{}x~K{}x~Kw~|L{}x~|Ww~}kw~}]{|l~}\\{|v~n{|w~}X{|s~U{}`~} -{|h~|$v~}#{}x~}t{}x" - "~}X{|}g~|^{}x~}m{}w~}y|}v~|j{|g~}y{}v~}({|w~|L{|w~| t{}w~L{}v~ u{|v~|Q{}x~}N{}k~}[{|e~|ab~`e~|N{}w~}]{}g~}Y{|i~" - "|Xv~|R{|g~}]i~|R{}v~M{}v~;y|5{|<{}w~}]{|w~|p{|v}|w{|x}|e{}v~dv~}f{}e~}|[{}d~|g{}d~}\\{}c~}`{}w~}M{}c~}|g{}w~}hv" - "~|Y{}w~}M{}w~}W{}w~}kt~a{}d~}i{}w~|bv~|h{}w~|l{|s~b{}f~|^{}w~}M{}f~|`{}w~}iv~}e{|c~V{|v~|Uf~}W{}t~|[s~l{}t~|g{}" - "v~hv~}Z{|v~|[{}\\~}^{|w~}D{}w~N{|w~| sj~{}w~|_w~}{|m~|Y{}i~|]{|m~|{|w~|^{|f~|Xw~}R{|m~|{}w~|`w~}m{|w~}Wv~Lv~Tw" - "~}o{}v~}Wv~_w~}mv~mv~hw~}m{|w~}^h~\\w~}{k~|\\k~y|w~|Zw~}Qg~}V{|n~Zk~{}w~|Y{|s~|Y{}u~}q{|t~a{|v~|o{}v~W{|u~|W{}d" - "~Uw~}Lw~|M{|w~| p{|l~|ys~be~}\\{}v~x}u~V{}j~}Z{|h~}^w~}m{|w~}X{|u~|Y{|w~}k{}w~}b{|w~}m~|s~h{|m~xm~|b{}g~|^{|w~" - "}pr~a{|f~}^w~}{k~|\\{}j~}R{|r~}Y{}l~}a{}Z~}d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~U{}f~|!{}w~ tt~| w{|t~} uv~" - "}R{}i~`{}b~}`{|?{|w~}Lw~|Fw~}&{}t~w}t~} A{}t~w}t~} V{|Z~} ${|w~}m~|s~Xx~}w{|x~} m{|x~}b{}x~hw~lk~k{|x~}b{}x~" - "fv~}U{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}w~}d{}w~}r{}w~}k{|b~f{}d~|c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}" - "w~}M{}w~}M{}w~}Z{|d~}|`{}w~|l{|s~b{}f~|^{}f~|^{}f~|^{}f~|^{}f~|`{|~|f{|~}f{|w~|}f~|]f~}]f~}]f~}]f~}V{|v~|V{}w~}" - "Mw~}xl~}_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|ii~w{|f~e{}i~|]{|f~|^{|f~|^{|f~|^{|f~|Wv~Lv~Lv~Lv~R{}l~" - "}[w~}m{|w~}^h~Zh~Zh~Zh~Zh~){|f~Zk~{}w~|^k~{}w~|^k~{}w~|^k~{}w~|X{|u~|Ww~}{k~|V{|u~| H{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|" - "j~|S{}x~}M{}u~} I{}Ax~} pw~|Lw~|L{|y~|Jy~|Vw~}kw~}[{}o~|[{}w~}mv~Wt~}T{|}b~} +{}l~}\"v~}#w~|tw~|U{|}l~}]{|w~" - "ko~|h{|j~}|w{}u~({}w~L{}w~ s{}w~Lv~| u{|v~|Qw~}M{|m~}Z{|e~|ab~`g~}|M{}w~}]{}h~|W{|k~W{}v~P{|i~|\\k~}P{}v~Mv~| " - "i{}w~}\\{}w~Jv~}d{}v~f{}g~}|X{|}h~}e{}g~}|Z{}c~}`{}w~}L{|}g~}|e{}w~}hv~|Y{}w~}M{}w~}W{}w~}jt~b{}d~}i{}w~|bv~|h{" - "}w~|ks~a{|i~}\\{}w~}L{|i~}^{}w~}i{|v~|e{}f~}U{|v~|T{}i~|Ut~Z{}u~}l{|t~g{|v~|h{|v~|[{|v~|[{}\\~}^{|w~}D{|w~N{|w~" - "| s{|l~|{}w~|_w~}x{}q~}|W{|j~|[{}p~|y{|w~|]{|g~|Xw~}P{}q~}|y{}w~|`w~}m{|w~}Wv~Lv~Tw~}n{|v~}Xv~_w~}mv~mv~hw~}m{" - "|w~}]{}l~}[w~}{|m~|Zm~|{|w~|Zw~}Qh~|T{|o~Z{|m~|{}w~|Xs~X{}u~|pu~}av~}m{}w~}Wu~V{}d~Uw~}Lw~|M{|w~| o{|n~|w{}u~b" - "f~}Z{}p~}T{}l~}X{|i~}^w~}m{|w~}Wu~Xv~|k{|v~b{|w~y|o~|{}t~g{|o~|x{|o~}`{}i~|]{|w~}p{}s~_{}j~}|]w~}{|m~|Z{}l~}P{|" - "s~}X{}n~}`X~d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~T{|i~} {{}w~ sv~| u{|v~} tv~}Q{}j~`{}b~}#{|w~}Lw~|G{|w~}${" - "}m~} ={}m~} T{|Z~} ${|w~y|o~|{}t~Xx~}w{|x~} mw~|b{}x~i{}x~|lk~kw~|b{}x~g{|v~Tv~}d{}v~jv~}d{}v~jv~}d{}v~jv~}d" - "{}v~jv~}d{}v~k{|v~|d{|v~rv~|k{|b~e{|}h~}a{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}w~|ks~a{|i~}[" - "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~" - "|_{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~|i{|l~}u{|g~d{|j~|\\{|g~|]{|g~|]{|g~|]{|g~|Wv~Lv~Lv~Lv~Q{|}p~}|Zw~}m{|w~}]{}l~" - "}X{}l~}X{}l~}X{}l~}X{}l~}){|w~}l~}Y{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|Wu~Vw~}{|m~|Tu~ E{|}p~}|V{|}p~}|V" - "{|}p~}|V{|}p~}|V{|}p~}|Qw~}Lu~| i{}y~| q{|w~}M{|w~}K{|}I{|}Uw~}kw~}Y{|y}w~y}|Yv~|m{}w~|X{}u~|Q{|}e~} *{|}p~" - "}|!v~}#w~t{|w~Py|x}y~x}y|[w~|j{}r~|e{|n~}|t{}u~){|w~|N{|w~| s{}w~Lv~ t{|v~|R{|w~|L{|}p~|Y{|e~|ab~`y|}l~}|K{}w~}" - "]{|}k~|S{}o~|Vv~}N{|m~}Z{}n~}|O{}v~Mv~ h{}w~}[v~L{|v~|d{|v~|g{}k~y}y|T{|}m~}|c{}m~x}y|W{}c~}`{}w~}J{|}k~}|c{}w" - "~}hv~|Y{}w~}M{}w~}W{}w~}it~c{}d~}i{}w~|bv~|h{}w~|k{|t~_{|m~}|[{}w~}J{|l~|]{}w~}h{}w~}c{|}k~}|T{|v~R{|}m~|S{}v~}" - "Z{|u~|kt~gv~}f{}v~[{|v~|[{}\\~}^{|w~}Cw~|O{|w~| q{}p~}x{}w~|_v}vy}w~y}|S{}m~}Xy}w~y}|w{|w}|[{|l~}|Vw~}N{|}w~y}" - "|w{}w~|`v}lw}|Wv~Lv~Tv}m{|u}Yv}_w~}mv~mv~hw~}m{|w~}\\{|n~|Zw~}x{}q~}W{}q~}|y{|w~|Zw~}Q{|}l~}P{|y}s~X{}q~}x{}w~|" - "X{}u~}X{|u~o{}v~|b{}w~}kv~}X{}w~}V{}d~Uv~Lw~|M{|w~| n{|}q~}u{|}w~bv~{}o~}|X{|r~|R{|}p~}|U{}l~}|^w~}m{|w~}W{}w~" - "}Xw}|i{|w}b{|w~|{|q~|y{|t~f{|q~|v{|q~|^{|l~}[{|w~}os~]{|}o~}|[w~}x{}q~}W{|}p~}|M{|}v~}W{|p~|`{|X~|e{}c~}_{}w~}V" - "k~v{}l~|^{|v~Y{}w~}hv~|Z{|v~R{|m~}| y{}w~ rx~| s{|x~} sv~}P{|}n~}|`{}b~}#{|w~}Lw~|Ty|pv~|\"y|}u~}y| 9y|}u~}y| " - "R{|Z~} ${|w~|{|q~|y{|t~Xx~}w{|x~} y}| q{}x~}aw}j{|w~kk~l{}x~}aw}gv~}U{|v~|d{|v~|l{|v~|d{|v~|l{|v~|d{|v~|l{|v~|" - "d{|v~|l{|v~|d{|v~|l{|v}bv}|t{}w~}j{|b~c{|}m~}|_{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|Z{}w~|k{" - "|t~_{|m~}|X{|m~}|X{|m~}|X{|m~}|X{|m~}|.w~}v{|}n~}|X{|}m~|X{|}m~|X{|}m~|X{|}m~|S{|v~|V{}w~}Mv|wy|}u~y}|Z{}p~}x{}" - "w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|g{}o~|r{|l~}|a{}m~}Y{|l~}|Y{|l~}|Y{|l~}|Y{|l~}|U" - "v~Lv~Lv~Lv~O{|y}v~y}|Xw~}m{|w~}\\{|n~|V{|n~|V{|n~|V{|n~|V{|n~|(w~|{|n~|V{}q~}x{}w~|\\{}q~}x{}w~|\\{}q~}x{}w~|\\" - "{}q~}x{}w~|W{}w~}Vw~}x{}q~}R{}w~} B{|t}|P{|t}|P{|t}|P{|t}|P{|t}|Nw~} 3{|~} ;f| '{|y}w~}y| 8{|y~|X{|x~}" - "h{|}w~}|ay|y}w~y}| rw~}N{}w~ ?{|w~| D{}w~I{|y}w~y}|%b|\\{|x}u~y}|!y|y}u~y}y|O{|y}w~y}| {{y|}u~y}|Vy|y}v~}y| u{|" - "w~| B{|v~| 1{|y}u~y}| o{|x}u~y}y| Fv~| 7y|y}v~y}| {{y|y}q~|#y|y}u~y}y| {{|y}v~y}y| a{|w~}C{}x~}O{|w~| oy}" - "v~}|vv|!{|}t~y}|!{|y}t~y}|Sv|Av~\"v|Lv~ Rv|mv|mv|hv|lv|Z{|y}u~}|Xw~}v{|}w~y}|T{|}w~y}|w{|w~|Zv|Ny|y}u~y}| {{|y}" - "w~}|uw|W{|u}|Wv}|o{|v}av|ju|Xv~| sv~Lw~|M{}w~| ly|}v~}|Uv~yy|}v~y}|S{|y}~y}|N{|y}v~y}|Qy|y}v~x}|[v|m{|w~}W{|w~" - "|#{|w~|x{|}w~}|v{|}y~y}c{|y}x~y}ry}x~y}|Z{|y}s~}y|G{}w~}|Zy|v~}|Ww~}v{|}w~y}|T{|y}v~y}| x{|y}w~}| Ry|y}v~y}|" - " Zy| rv~}M{|y}u~}|]`| Iw~|T{|y~}|u{|u~ 5{|w~|x{|}w~}|v{|}x~}Wx~}w{|x~} {}y~} r{|y}|Kw~|L{|y}|Hv~| E" - "{|y}u~y}| qy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|+{|y~}r{|y}v~y}|R{|y}v~y}y|S{|y}v~y}y|S{|y}v~y" - "}y|S{|y}v~y}y| oy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|d{|}v~y}|n{|y}u~y}y|\\{|}t~y}|U{|y}" - "t~y}|T{|y}t~y}|T{|y}t~y}|T{|y}t~y}|Rv|Lv|Lv|Lv|!v|lv|Z{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|'{}x~|w{|y}u~" - "}|S{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Vv~|Vw~}v{|}w~y}|Qv~| Mw~| K{|y~| e{|w~Nw~" - "| ?{}w~ Cw~} .{}w~ @{|v~|d{}| Kv~| !u~| J{|w~}C{|w~O{|w~| 9w~} Iv~ bw~}9{|w~| X{|v~ rv" - "~Lw~|M{}w~| <v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|u~y}s~| 5{|w~|Ax~}w{|x~} {" - "{x~| 0{|v~ ?{}y~} R{|} 5x~| O{|y~} &{|v~Uw~}D{|v~ Lw~| K{|y~| d" - "{}x~}P{}w~ >w~| D{|w~| .w~| ?{|v~}g{|x~| M{|v~ {|u~| K{|w~}Bw~|P{|w~| :{}w~} Iw~} bw~}9{" - "|w~| X{}w~| r{}w~|Mw~|Mv~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|l~| 4{|w~" - "|Ax~}w{|x~} {{}y~} /v~| ?x~| f{|x~ M{} %{}w~|Uw~}D{}w~| Lw~| K" - "{|y~| d{|w~Pw~| ?{|w~ C{}w~ .{|w~ ={|u~}|l{|u~| N{}v~ {{|u~| L{|q~}H{}x~}V{}q~| :v~| Iw~}" - " bw~}9{|w~| Xv~ q{}w~}Mw~|N{|v~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|}o~}| " - " 3{|w~|Ax~}w{|x~} {{|x~| 0v~}m{} N{|x~ e{}y~} Rv~Tw~}Dv~ S{}x~x{|w~| " - " K{|y~| c{}x~}R{}x~} >{|x~| Cw~} .{|x~| ;{}t~}|sy|}t~| N{|v~} y{|u~| M{|q~}H{|w~V" - "{}q~| ;{}v~ I{|w~} bw~}9{|w~| Y{}w~} q{|v~}|Ow~|P{|}v~} ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} " - " )v~}Iy~} gw~|Q{|y}v~y}| 1{|w~|Ax~}w{|x~} yx~| 0{}v~|p{|~} N{|x~| f{|x~ " - " S{}w~}Tw~}E{}w~} S{}x~|y{|w~ J{|y~| bw~|Sw~| >{}y~} K{}y~} 9{|p~x}q~}| N{|u~" - "| x{|u~ M{|q~} y{}q~| K{|}|p{|u~| I{}w~| bw~}9{|w~| Z{|v~ o{}q~}Tw~|U{|p~ :v~ S{|w~}W{|w~|#{|" - "w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~|Aw|vx| y{|x~} 0{|u~|s{}x~} N{|x~| " - " f{|x~| U{|v~Sw~}F{|v~ R{|x~}y{}w~ J{|y~| b{|x}|T{|x}| w{}g~}| Q" - "x|y}u~} v{|u~ N{|p} yp}| K{|x~}y|wy|}u~} J{|}v~ aw~}9{|w~| \\{|}v~} nq~}Tw~|U{|q~| :v~ S{|w~}" - "W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~| :{|}w|}w~| /t~y}x|y}v~} U{|}|x{|w~| " - " f{}x~| W{|}v~}Sw~}H{|}v~} Qq~| J{|y} *{|}l~}| O{}q" - "~ tt| `{|i~} Lr~| aw~}9{|w~| `{}q~ l{}s~}Tw~|U{|s~}| 9v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~" - "} )v~}Iy~} gw~| W{|w~| :{|q~ .{|i~} U{|q~ ly}w|}w~| [{}q~Rw~}" - "L{}q~ P{}r~ M{|y}u~y}y| L{}r~| R{|j~} Ks~} `w~}9{|w~| " - " `{}r~| jy|v}|Tw~|U{|u}| 6v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy}| gw~| W{|w~| :{|r~| " - " -{|k~}| U{|r~} l{}r~} Z{}r~|Rw~}L{}r~| O{}t~ " - " k{}t~} -{|`}| `{|}m~}| Jt~} _w~}9{|w~| `{}s~| :w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}" - "w~Uw~} )v~} d{|w~| 9y}w~y} ){}o~}| S{|}u~}| k{}r~ Y{}s~|Qw~" - "}L{}s~| M{}w~} j{}w~}| +{}`~} ]{|x}v~y}| Gw~y} ]w~}9{|w~" - "| `{}v~}| 8w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} g{|w~| 8{|}v~y}| Ly| " - " g{|y}w~}| X{}v~}|Ow~}L{}v~}| Iy| " - "l{}`~} Ww~| " - " L{}`~} Ww}| " - " r{" }; - - // Define a 104x128 binary font (huge sans). - static const char *const data_font_huge[] = { - " " - " " - " " - " " - " " - " " - " " - " " - " FY AY " - "'Z ;W @Y @Y 'Z Y @Y (Z :Y ?Y (Z 0Y ?Y (Z >X " - " " - " " - " " - " " - " )X AX '\\ )XAV 7YDY -] BY BY '[ +YEY 2X AY (\\ -YDY 'XAU 3Y AY (\\ )XAV 8YD" - "Y LY AY (\\ ,YEY #Y " - " " - " " - " " - " (X CX '^ +[CU 6ZEY .` C" - "X CY '] -ZEZ 2X CY (^ .ZEZ )[CU 2Y CY (] *[CU 7ZEZ LY CY (] -ZEZ %Y " - " " - " " - " " - " " - " 'Y EY '^ ,^FV 6ZEY /b CX DX '_ .ZEZ 2Y DX '_ /ZEZ +_FV 1X CX (_ ,^FV 7ZEZ " - " KX CX (_ .ZEZ &Y " - " " - " " - " " - " %Y GY '` .aHV 6ZEY 1e DY FX" - " 'a /ZEZ 1Y FX '` /ZEZ +aHV 0X EX '` .aHV 7ZEZ JX EX (a /ZEZ &X " - " " - " " - " " - " " - " #X GX 'XNX 0dKW 6ZEY 1f DY HX &WMX 0ZEZ 0X GX 'XMW 0ZEZ ,dLX /X GX 'WMX 0dLX 7ZEZ" - " IX GX 'WMX 0ZEZ 'X :T " - " " - " " - " " - " ;X IX 'XLX 1o 5ZEY 2ZLY " - " CX IX &WKW 0ZEZ /X HX (XLX 1ZEZ ,o .Y HX (WKX 1o 6ZEZ IY IY (WKW 0ZEZ (X <Z " - " " - " " - " " - " " - " =X KX 'XJX 3WKd 5ZEY 3XGX CX JX 'WIW 1ZEZ .X JX (XJX 2ZEZ -WKd -X " - "IX (WIW 2WKd 6ZEZ HX IX (WIW 1ZEZ )X =^ " - " " - " " - " " - " >X MX &WH" - "W 3VHa 4ZEY 3WDW CX LX 'WGW 2ZEZ -X LX 'WHW 2ZEZ -VHa +X KX (XHW 3VHa 5ZEZ GX KX (WGW 2ZEZ )X " - " ?b " - " " - " " - " " - " ?W MW &WFW 4VF^ 3ZEY 4WBV BW MX 'WEW 3ZEZ ,W M" - "X 'WFW 3ZEZ -VF^ )X MX 'WFW 4VF^ 4ZEZ FX MX 'WFW 3ZEZ *X ?d " - " " - " " - " " - " " - " ?W X 'WDW 5UC[ 2ZEY 4VAV AW X &WDW 4ZEZ +W NW 'WDW 4ZEZ -UC[ 'W MW 'WDW 5UC[ 3ZEZ " - "EW MW 'WDW 4ZEZ +X ?f " - " " - " " - " " - " @X \"X 'WBW 6UAW 0ZEY 4V@V B" - "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ .VAW $W W 'WBW 6UAW 1ZEZ DW W 'WBV 4ZEZ +W >f " - " " - " " - " " - " " - " ?X #W 'W@W U?V AX #W &W@V NX #W &V@W 9W \"W 'W@V .W " - "\"W 'W@V !W >XHX " - " 3Y " - " " - " " - " 6W $W &V>V U?V @W $W &W>V " - " NW $X 'V>V 8W $X (W>V /X $W 'W>V #W >XFX " - " 5Z " - " " - " ,Z " - " GZ " - " #U?V NY 7Z ,X CVCW MY " - " 7Z ,X $Z 7Z ,X >Z 6Y ,X 4Z 7Y +W 7Y @Z " - " " - " +Z " - " " - " HY \"U?V " - " MY 8Y ,Y CVBV LY 9Z ,Y #Z 9Z ,Z >Z 8Y ,Y 3Y 8Z ,Y 9Y " - " ?Z " - " *Y " - " " - " IY !U?V " - " LY :Y ,[ $R>U ,V@V MZ :Y +Z #Y 9Y +Z ?R" - ">U 8Y 9Y +Z %S?U HY :Z ,[ ;Y ?[ " - " " - " )Y " - " 8U " - " 9Y V@U JY <Y" - " +[ 'XAU ,V@V LY ;Y +\\ #Y ;Y +\\ CXAU 7Y ;Z ,\\ )XAV HY ;Y +[ <Z " - " ?U ;T $W /W " - " 8e !f LY Y LX " - " L] :Y <Y NX 0X >Y @Y /X 0Y K` .X " - " ^ =ZEY @Y " - " NVAV <P -X +Y =Y +] )[CU 7YDY 4V@V KY =" - "Y +] ,YDY 5Y =Y *] .YDY 5[ M[CU 6Y <Y ,] *[CV 7YDY Y =Y +] ,YEZ !Y =Y FYDY 8X " - " EU :T %W .X " - " 9e !f KY !Y LY \"a :Y " - "<Y NX 0X >Y E^ /X 0_ %f 1] 'c " - " @ZEZ AY MV" - "CW <R 4a .Y >X *^ +]DU 7ZEZ 5U>U JY ?Y *^ -YEZ 4Y " - " ?Y *^ .ZEZ 5[ ]DU 5Y >Y +^ ,]DU 6ZEZ Y ?Y +_ .ZEZ \"Y <Y FYEZ :[ FU " - " 7Y -T 7W#W <Y 9X -W DU KY HZ \"\\ 4Z M[ \"" - "Y LZ +\\ 8] >Z G[ G\\ @e !f JX !Y " - "LY %d :Y <Y NX 0X >Y Ha /X 0b *j L] D_ " - " +g A[ LY 8Z -ZEZ \"Y 1o )V FX NZ FY " - "%Y ,X NX*Z NW 3WEW H\\ #[ !Z \"[ \"[ \"[ G[7T 8g 0Y " - "@Y +_ ,_FV 7ZEZ 5U>U IY @Y +` .YEZ 3X ?X *` /ZEZ 4[:P 8_FV 4X ?Y +` ._EU 6ZEZ NX @Y *_ .ZEZ #Y ;Y" - " FYEZ ;] GU <b 1T :]'X @b >W ,X " - " FV a \"d -g >d (d +b %b 4f Bg Ie \"e \"h " - " Ge !f IX \"Y LY &e :Y <Y NX 0X >Y Jc /X 0c " - " -n $g I` .j >a ;e HU .U +b Ac 2ZEZ 'b " - " 5o -] Na (c KY .Y #_ 8Y!W'Y\"X.c$X 3XGX Mf -e +d " - ",e ,e ,e \"e=V ;k 1Y BY +XNW .aGV 7ZEZ 5V@V HX AY +XNW .YEZ 3Y AY *WNW /ZEZ 4\\>T 9`GV 3" - "X AY +XNW .`GV 6ZEZ NY AX *XNW /ZEZ $Y :Y FYEZ <_ IU (Q LZ 4Z2Z 1Q " - " &g %Z +XCX MT <a)W Ah $X HX +X GV GX 3e )_ /j 4n L] ?y /i C~S =i 0g " - " +g L\\ 8t (m Ks 2~R E} <o HZ(Z :Z \"Z 4Z,] LZ 2_'_(^-Z Ck :q 0k ?q *n J~d'Z(Z*Z LZ=Z.\\.Z7Z(Z([$Z'~^" - " @e 3X Ff )\\ MY #Y LY (g :Y <Y NX 0X >Y Kd /X 0e 0p " - " (m Lb 1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik LW DX DW /i ?Y(Y 4h 5ZEZ" - " ,\\ ,h 7\\ -o .` $f -h NY No %_ %c @_\"X-_\"W0h&W .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S " - "H~R H~R 6Z !Z !Z \"Z :r 8^,Y Bk 2k 2k 2k 2k (kAX+Z(Z#Z(Z$Z(Z$Y'Y&[%[ MZ Im 1X CY *WMX /bHV 7ZEZ 5V@V G" - "X CY *WLW /YEZ 2Y CY *WLW 0ZEZ 3[AW :bHV 3Y BX *WLW 0bHV 6ZEZ MY CX *XMX 0ZEZ $X 9Y FYEZ " - " =a M~i 7U (Q N_ 9_8_ 3R )k 'Z +XCX +X@X 4T >e,X Cl &X IX *X GV " - " GX 5i 0d 2p ;u !^ ?y 2o F~S @n 4j /l N\\ 8x .r Nx 7~R E} >t KZ(Z :Z \"Z 4Z-] KZ 2_'_(^-Z" - " Ep =t 5o Au 1u N~d'Z(Z)Z MZ<Z/\\/Z5Z*['[&Z&~^ @e 3X Ff )] MY $Y LY )h :Y <Y NX 0X >Y " - " Le /X 0e 1r +r c 3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko " - " Y EX EY 2m @Y)Y 6l 7ZEZ 0e 2k >e 1o 0c 'j /i X !r (b 'g Eb\"W0c#X0i(W -" - "\\ $] #\\ $] #\\ (f 6~b r F~S I~S H~R H~R 6Z !Z !Z \"Z :w =^,Y Ep 6p 7p 7o 7p ,oDY+Z(Z#Z(Z$Z(Z$Y'Y%Z%Z LZ Kp" - " 1X DX *WKW /WMYJV 6ZEZ 5V@V GY EY *WKX 0YEZ 1Y EY *XKW 1ZEZ 2[EZ :WMZKV 1Y DX *WKX 1WLYKW 6ZEZ L" - "Y EY *WKW 0ZEZ %X 8Y FYEZ >c M~h 7T (S !a <b:b 6S %| $o " - ")Z +XCX +W?W 3T ?g.X Dp (X IX )X HV HY 6l 7i 5t <v #_ ?y 3p F~S Aq 8n 3p (Y $^ 9z 2v!{ :" - "~R E} Az NZ(Z :Z \"Z 4Z.] JZ 2`)`(_.Z Gt ?w :s Cx 5x!~d'Z(Z)Z N[<Z/\\/Z5[,[%Z'[&~^ @e 2X Gf *_ MX $Y " - "LY )h :Y <Y NX 0X >Y >X 8f /X 0f 3t -s c " - " 4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms #[ FX F[ 4n @Y*Y 6m 7ZEZ 3k 5l Bk 4o 1f )k 0k #" - "X #u (b (i Fb#X0c#W/k+X .^ %] $^ %] $^ (d 5~b\"v H~S I~S H~R H~R 6Z !Z !Z \"Z :{ A_-Y Gt :t ;t ;s ;t " - " 0sGY*Z(Z#Z(Z$Z(Z$Y'Y$Z'[ LZ Ls 2X FX *WIW 1WJc 6ZEZ 4VBV EY FX *XJW 0YEZ 0X EX )WJW 1ZEZ 1[I^ <WJc 0" - "X EX )WJW 2WJZNW 5ZEZ KX FY *WIW 1ZEZ &X 7Y FYEZ ?d M~h 8U )T #e ?d=e 8U " - " *~Q &r *Z +XCX +W?W 3T @i/W Dq (X JX (X HV HX 7o <m 7x >x %_ ?y 5r F~S Ct :p" - " 6s /e *^ 9| 6z#~ =~R E} B}!Z(Z :Z \"Z 4Z/\\ HZ 2`)`(_.Z Iw @y >w Ez 9z!~d'Z(Z)[ Z;Z0]/Z4Z,Z$[(Z%~^ " - "@e 2X Gf +a MX %Y LY *i :Y <Y NX 0X >Y >Y 9f /X 0g 5v " - " 0u d 6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w &] GX G] 6U &o ?Y+Y 7X )n 7ZEZ " - "6p 7m Eo 6o 2h *l 1l %X #v (b )k Gb$X/c$X/l,W -^ &_ %^ &_ %^ 'b 4~b$z J~S I~S H~R H~R 6Z !Z " - "!Z \"Z :~ D_-Y Hw =v >w >w >w 4wIX)Z(Z#Z(Z$Z(Z$Y'Y$[)[ KZ Mt 1X HX )WHW 2VHb 6ZEZ 4WDW DX GX )WHW 1YE" - "Z /X GX )WHW 2ZEZ 0[M` ;VHb /X GY *WHW 3VHb 5ZEZ JX GX )WHW 2ZEZ 'Y 7Y FYEZ ?e M~f " - " 7U )U %g Bh@g :W .~T 't +Z +XCX ,X@X 3T Ak1X Er (X JX 'X IV HX 8q" - " =m 7y ?y '` ?y 6s F~S Dv <r 8u 4m /_ 9~ :~%~Q ?~R E} D~Q\"Z(Z :Z \"Z 4Z0\\ GZ 2`*a(`/Z Jz Bz Az F{ " - ";{!~d'Z(Z(Z Z;Z0^0Z3Z.[#[*Z$~^ @X %X :Y ,c MX &Y LY +^ .Y <Y NX 0X >Y >Y " - " :] %X &] 5]C\\ 1v Nc 7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y (_ H" - "X H_ 7U 'p ?Y,Y 6X *o 7ZEZ 8t 9YH] Ht 9o 3i *XG[ 1VE[ &Y %x (b *[I[ Hb$W.c%X.VE[-X " - " ._ &_ %_ '_ %_ '` 4~c%} L~S I~S H~R H~R 6Z !Z !Z \"Z :~Q F`.Y Jz @z Az Ay Az 7zKX(Z(Z#Z(Z$Z(Z$Y'Y#[*Z JZ Na" - "J_ 2X IX )WGW 2VG` 5ZEZ 4XFX CX IX )WFW 2YEZ .X IX )WFW 3ZEZ /j 8VG` -X HX *WFW 4VG` 4ZEZ IX IX " - ")WGW 2ZEZ 'X 6Y FYEZ ?XKX M~f 7T )W 'i DiAi ;X 1~V (w -Z " - "+XCX ,X@X 3T AZI[2W Es (X KX &X IV HX 9s >m 7z @z )a ?y 7t F~R Dx >t 9v 8s 2` :~P <~Q&~S" - " A~R E} E~T$Z(Z :Z \"Z 4Z2] FZ 2a+a(`/Z K| C{ C} H| =|!~d'Z(Z(Z!Z9Z1^1Z2[0[!Z+[$~^ @X $X ;Y -e MX 'Y " - "LY +[ +Y <Y NX 0X >Y >Y :[ #X #Z 6\\?[ 2v F\\ " - " 8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#} +` HX Ia 8U (q >Y-Y 6X +p 7ZEZ 9bMb ;U@Y JbMb :" - "n 3ZIZ +T@Y 2R>Y 'X %y (XLV +ZEZ IXMW%X.YMW%W-R>Y.W -` '_ &` '_ &` '` 4~c'~R N~S I~S H~R H~R 6Z !Z " - "!Z \"Z :~S Ha/Y K| B| C| D} D| 9|MX'Z(Z#Z(Z$Z(Z$Y'Y\"Z+[ JZ N]B\\ 2X JX *WEW 3UE_ 5ZEZ 3YJY AX JW )WE" - "W 2YEZ -X KX (WFW 3ZEZ .f 5UE_ ,X JX )WFW 4VF_ 4ZEZ HX KX )WEW 3ZEZ (X 5Y FYEZ @YJW M~" - "e 7U *X (j EkCk =Y 3~X )x -Z +XCX ,W?X 3T BYEY3X Ft (X KX %X JV " - " IX 9u ?m 7{ A{ *a ?y 8u F~R Ez @v :v :w 4` :~Q >~S'~U C~R E} G~V$Z(Z :Z \"Z 4Z3] EZ 2a+a(a0Z M~P D" - "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X ;Y .g MW 'Y LY +Y )Y <Y NX 0X >Y " - " >Y :Z \"X \"Z 7[=Z 3aE[ E[ 9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$" - "~P -b IX Jc 9U )r >Y.Y 5X ,]DX 7ZEZ ;\\>\\ <R;X M]>\\ 0XDX ,R=Y MX (X %hEW (SG" - "V ,YAY JSHW%W-SGW&X GX/W ,` (a '` (a '` (a 5~d(~S N~S I~S H~R H~R 6Z !Z !Z \"Z :~T Ia/Y L~P F~P F~P F~P F~P" - " <~X&Z(Z#Z(Z$Z(Z$Y'Y\"[-[ IZ \\>Z 1X LX )VCW 4UD] 4ZEZ 2f ?X LX )WDW 3YEZ ,W KX )WDW 4ZEZ -b 2UD] *W" - " KX )WDW 5UD] 3ZEZ GW LX (VCW 4ZEZ )X 4Y FYEZ @XIX M~d 7U *Y *l GmDl ?[ " - " 6~Z *`C\\ -Z +XCX ,W?W 2T CYCY5X E]CZ (X LX $X JV IX 9]E^ @m 7aGb B^Ec ,b ?y " - "9aF[ F~R E_C_ B_E^ ;]E_ ={ 7b ;~R @cBb'~V D~R E} HeBc$Z(Z :Z \"Z 4Z4] DZ 2b-b(a0Z NbCb E} GbCb J~ Aa" - "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X ;Y /i MW (Y LY ,Y (Y <Y NX 0X >Y >Y " - " :Y !X !Y 8[;Z 1\\ 0\\:U D[ ;Z<Z 4b 8~S E~R M~R 4Z :~]+[;Z;Z%bCb " - "/d JX Ke :U )]BW =Y/Y 5X ,[?U 3Z8[ &W NZ7Z 2XBW EX LW )X %iEW KV -Y?Y @W&X" - "!W&W EW0X -b )a (b )a 'a )a 5~d)dCb N~S I~S H~R H~R 6Z !Z !Z \"Z :~V Kb0Y MbCb HbCb HbCb HbCb HbCb >bCh%Z(Z" - "#Z(Z$Z(Z$Y'Y![.Z HZ Z;Z 1X NX )WBV 5VBZ $e >W MX )WBW !X MX )WBW #` /UBZ (W MX )WBW 6UBZ " - " 9X MW (WCW MX 3Y GXHW M~d 8U *[ +m HnFn A] 9~\\ +^=Y" - " -Z +XCX -X@X 2U DXAX5W E\\=V (X LX #X .R@V?Q ,X :\\A\\ @m 7\\>_ CY<_ -c ?y :^=V F~Q E]>^ D]@] " - " <Z@^ @~P 9b ;Z=d Aa;^'Z>j E~R E| Ha8^$Z(Z :Z \"Z 4Z5] CZ 2b-b(b1Z `<_ FZ@d I`=` K[@d C_:Z ~b&Z(Z'Z#Z8Z2`" - "2Z0[4[ LZ/[\"~^ @X #X <Y 0\\N] NX )Y LY ,Y (Y ;X NX 0X >Y >Y ;Z " - "!X !Y 8Z9Y 6d 4[5R CZ ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=` 1f KX Lg " - " ;U *\\=T =Y0Y 4X ,Z;R 5Z3Y &W !Y3Y 3W@W EW LX *W %jEW KV -X=X @W'X W'X EX1W ,b " - "*b (b )b )b )b 7ZH~R)a:] N~R H~R G~R H~R 6Z !Z !Z \"Z :Z>j Lb0Y N_<` J`<_ J`=` J`=` J`=` @`=e%Z(Z#Z(Z$Z(Z$Y'Y" - " Z/[ HZ !Z9Y 0W X )WAW 6VAW \"d <W X (VAW X X (V@V &a .VAW &X NW (V@V 6UAW 6X X )WAW " - " NW 2Y N\\ #[ \"\\ #\\ #[ MXHW L~b 7U +\\ ,n IoGp C_ ;~] ,]:X -Z " - "+XCX -X@X 8c LX@X7X E[:T (X MX \"X /TAVAT .X :\\?\\ Am 7Y9] CT4] .c ?Y J]8S Z E\\;\\ E]=[ " - " <W;\\ B~T ;b ;Z7_ C_5['Z7e GZ MZ '`3[$Z(Z :Z \"Z 4Z6] BZ 2b-b(b1Z!_8^ GZ;` K_9_ LZ:` D]5W 3Y 9Z(Z&Z$Z7Z3`3Z." - "Z4Z JZ0Z \\ ?X #X <Y 1\\L] NX *Y LY ,Y (Y 8X >Y >Y ;Y X !Y " - " 8Y8Y 6f 6Z2P BY <Z9Z 7c 7\\ Z (`;` >j BZ(Z+[;Z;Z'_9_ 3h LX Mi <" - "U *[:R <Y2Z 4X -Z8P 6Y/X 'W #Y/Y 6W>V EW KW +W %kEW KV .X;W @W'W NW(X CW2X -c *c )b " - "*c )c +c 7ZHZ 2_5[ NZ !Z Z !Z >Z !Z !Z \"Z :Z7d Mc1Y ^8_ K^8^ L_8^ L_9_ L^8_ B_9b$Z(Z#Z(Z$Z(Z$Y'Y [1[ GZ !Z" - "8Y 0W !W (V?W I` :X !W (V?W X \"X (W@W *d EX !W (W@W 0X \"X (V?W !W 1Y #d ," - "e +d +d ,e #XHW LZ#Z 7U +] -o KqHp C_ <c 2]7V -Z +XCX -W?X <l#X?X7W E[7R " - "(X MX \"Y 0VCVCV .X :[<[ B\\IZ 7V5] DQ0] 0XNZ ?Y K\\4Q !Z E\\9\\ F\\;[ =U8[ DdAc =d <Z5^ " - "E^1Y'Z3b HZ MZ (_/Y$Z(Z :Z \"Z 4Z7] AZ 2c/c(c2Z!]4] HZ9^ L^5^ MZ8^ E\\0T 3Y 9Z(Z&Z%Z6Z3`3Z-Z6[ J[2Z \\ >X #X " - " <Y 2\\J] NW *Y LY ,X 'Y 8X >Y >Y ;Y X X 9Z7X 6g 7Y" - " #Z =Y8Z 7d 7[ Z )_7_ Bp EZ(Z+[;Z;Z(^5^ 5j MX Nk =U +[7P <Z3Y 3X -Y " - " MX+W 'V $X+X 7V=W FW KW ,W $kEW KV .X;X AW(X NW(W BW2W ,d +c *d +c *d +c 7ZHZ 3^0X NZ !" - "Z Z !Z >Z !Z !Z \"Z :Z3a Nc1Y!^5] L]4] N^5^ N^5^ N^5] C^5_#Z(Z#Z(Z$Z(Z$Y'Y N[2Z FZ \"Z7Y /W #W (W>V H^" - " 8X #W (W>V NW \"W (W>W .h EW \"X )W>W 0W #X (V=V \"W 0Y &j 1i 0j 1j 1i &X <Z#Y " - " 7U +_ /p KrJr Ea >` .\\5U -Z +XCX -W?W =r'X>W8X EZ ;X NY !X 1XDVDX 2X " - " &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y L\\ 2Z E[7[ G\\9[ >S5[ F`7` ?YNY <Z3\\ F]-W'Z0` IZ MZ )^+W$" - "Z(Z :Z \"Z 4Z8] @Z 2YNX/XNY(c2Z\"]2] IZ7] N]2] MZ6] G\\-R 3Y 9Z(Z&[&Z6Z4XNW3Z-[8[ HZ3[ !\\ =X #X <Y 3\\H] N" - "W +Y LY ,X 'Y 8X >Y >Y ;Y X Y :Y6Y 7i 9Y \"Y " - " >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2] 6l NX m >U +Z !Y4Z 3X -Y NW(W (W " - " &X)X 8V<V +X DW LW ,W $lEW KV .W9W AW(W MW)X CW2W +YNY ,YNZ +YNY ,ZNY +YNY +YNY 9ZGZ 4^.W NZ !Z" - " Z !Z >Z !Z !Z \"Z :Z1` d2Y\"]2] N]2] ]2]!^2]!]2] E]2]\"Z(Z#Z(Z$Z(Z$Y'Y MZ3[ FZ \"Z6X .V $W 'V<V GZ " - " 5W $W 'V<V NW $W 'V<V 2m EW #W (V<V /W $W (W=W #W 0Y (n 6o 5n 5n 6n (X ;Z%Z " - " 7U ,a 0q LrJr Fc A_ ,\\2S -Z +XCX .X@X ?u(W=X:X DY :X NX Y 2ZFVFZ 2X " - "'X :Z9[ CR?Z 7R/\\ \"[ 1XMZ ?Y L[ 2[ F[5Z G[7Z >R4[ G^1^ AZNY <Z2[ G]*U'Z.^ IZ MZ )](U$Z(Z :Z \"Z" - " 4Z9] ?Z 2YNX0YNY(d3Z#]0] JZ6\\ N\\/\\ NZ5\\ G[ <Y 9Z(Z%Z&Z6Z4XNX4Z,Z8Z FZ4Z [ <X \"X =Y 4\\F] #Y " - "LY -Y 'Y 8X >Y >Y ;Y X Y :Y6Y 7j :Y \"Y " - " >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\ 8n X !o ?U ,[ Y5Y 2X -Y W&W )W 'W%W 9V" - "<V +X DW LW )mEW KV /X9X BW)X MW)W BW3X ,YMY ,YMY ,ZNZ -YMY +YNZ -YMY 9ZGZ 5]*U NZ !Z Z !Z >Z " - "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0] F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X +P " - " %_K[ CY *r 9q 8r 9r 9q *X ;Z%Z >Q JT ,b 0q MsKs Ge " - "C^ *[0R -Z +XCX .X@X @v)X=X:W CY :X Y NX 1[HVH[ 1X 'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y M[" - " 1Z EZ4[ I[5Z ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5" - "\\!\\-\\ Z4[ GZ ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z [ ;X \"X =Y 5\\C[ #Y LY -Y 'Y 8X >Y " - " >Y ;Y X Y :Y6Y 7k ;Y \"Z @Z5Y 9YLY 5[ #Z +\\.] J| KZ" - "(Z+[;Z;Z*\\-\\ :p !X \"q @U ,Z NY6Y 1X -X W#V *W (W#W :U;V +X DW LW )mEW KV" - " /X9X BW*X LW*X BW3W +YLY -YMY ,YLY -YMY ,YLY -YMZ ;ZFZ 5\\'S NZ !Z Z !Z >Z !Z !Z \"Z :Z-^\"e3Y#\\.]#].\\" - "#\\-\\#\\-\\#\\-\\ H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y /[G[ " - " DY +u =u <u ;u =u ,X :Y&Z >S LU ,c 1q MtLt Hf E] )[.Q " - " -Z +XCX .W?X Bx)X=X;X DZ :X X MY 0ZIVIZ /X 'X ;Z7[ 1Z AZ ![ 4XKZ ?Y MZ 0Z EZ3Z I[5Z " - "Z J])\\ CYLY =Z1[ I\\%R'Z+] KZ MZ +\\\"R$Z(Z :Z \"Z 4Z;] =Z 2YMX1XMY(YNZ4Z$\\,\\ KZ4[\"\\+[ Z4\\ I[ ;Y 9Z(Z$Z" - "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X =Y 6\\A[ $Y LY -Y 'Y 8X >Y >Y " - " ;Y X Y :Y6Y 7l <Y !Y @Y4Z :YLY 4[ $Z ,\\,] M~Q MZ(Z+[;Z;Z+\\+\\ <r \"X" - " #s AU ,Z MY7Y 1X -Y \"W!V :f (V!W ;U;V +X EX MW (mEW KV /W7W BW*W KW+X BW3X " - " +YLY .YKY -YLY .YKY -YLY .ZLY ;ZFZ 6\\%R NZ !Z Z !Z >Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP" - " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X /XC[ EY " - " -x @x >x ?x @x -X :Z'Z ?U MU -e 2q MtLt Ig E[ 'Z,P -Z +XCX .W?W By)" - "X<W;W CZ :X X MY .ZKVKZ -X (Y <Z5Z 1Z A[ !Z 4XKZ ?Y N[ 1Z DZ3Z IZ3Y NY K\\%[ EYKZ >Z0Z" - " J\\#Q'Z*\\ KZ MZ +[ Q$Z(Z :Z \"Z 4Z<] <Z 2YMY3XLY(YMZ5Z%\\*\\ LZ4[\"[*\\!Z3[ IZ :Y 9Z(Z$Z)[4Z6XLW5Z)Z<Z BZ8Z" - " !\\ :X !X >Y 7[>[ %Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" - "5Y 7UH_ <Z \"Z AY3Y ;YKZ 4[ %Z ,[*\\ N~S NZ(Z+[;Z;Z+[*\\ =\\NXM[ #X $\\MXN\\ " - " BU ,Z *P DY8Y 0X -Y #W NV @k )V NV <V;V +X EW NY )nEW KV /W7W BW+X KW+W CY4X +YKZ /" - "YKY .ZLZ /YKY .ZKY /YKY <ZEZ 7\\#Q NZ !Z Z !Z >Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z" - "(Z$Z(Z$Y'Y K[9[ Ct =Y3X /U@[ \"Q EY .z B{ " - "B{ Az B{ /X :Z'Y >V U -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[ '[ 8Z +XCX /X@X C`MTL_)W;" - "W<X CY 9X !Y LX ,ZMVMZ +X (X ;Z5Z 1Z A[ !Z 5XJZ ?Y NZ 0Z DY2Z J[3Z )Q Q JZ M[!Z FYJY >Z0Z " - "J[ 'Z)\\ LZ MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !" - "\\ 9X !X >Y 8[<[ &Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y5Y " - "7RB] =\\ $Z BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\ ?\\MXL[ $X %\\LXM\\ CU" - " ,Y *Q\"R DY9Y 0X -Y #V=_?V Cm *V LV <U;V +X FX \"[ (nEW KV /W7W BW+W JW,X F[3W *YJY 0Z" - "KZ /YJY /YKZ /YJY /YJY =ZEZ 7[!P NZ !Z Z !Z >Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z(" - "Z$Z(Z$Y'Y J[:Z Bw @Y6[ .Q<[ #S GY /`Da E`C" - "` DaD` C`Da E`C` 0X 9Y(Z ?X !U .h 4r NvNu Kk .c 9X.X 7^ 7Y1Y M[ &Z 7Z +XCX /X@X C\\" - "ITFY)W;W=X BY 9X !X KY +YNVNZ *X (X ;Z4Z 2Z @Z !Z 6YJZ ?Y Z /Z DY2Z JZ1Y ,T T MZ N[ NZ HZJ" - "Y >Z0Z K[ &Z(\\ MZ MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;" - "[ ![ 8X !X >Y 9[:[ 'Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" - "5Y %\\ =] %Y BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[ @\\LXK[ %X &\\KXL\\ " - " DU -Z +S$T EY:Y /X -Z %V?fBU Eo +VEg=V =V<V +X GX *b &nEW KV /W7W BW,X JW,W Nb2X +ZJY " - "0YIY /YJY 0YIY /YJZ 1YIY =ZEZ 8\\ NZ !Z Z !Z >Z !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$" - "Z(Z$Y'Y IZ;Z Ay BY9^ G[ %U HY 0]<^ G^=^ F" - "^<] E]<^ G^=^ 1X 9Z)Z @Z \"U .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[ &[ 7Z +XCX /W?X D[GTC" - "V)W;W=W AZ :X \"Y KY *j (X (X <Z3Z 2Z @Z !Z 6XIZ ?Y Z 0Z DZ2Z JZ1Z 0W V Y NZ KZ IYIZ ?Z0Z " - "K[ &Z(\\ MZ MZ -[ Z(Z :Z \"Z 4Z?\\ 8Z 2YKX5XKY(YLZ6Z&[&[ MZ3[%[&\\#Z2[ JZ :Y 9Z(Z#Z+Z1Z7WJW7Z&Z@Z >Z<Z ![ 7X" - " X ?Y :[8[ \"\\ 3YBZ \\ ,ZAY 4\\ &Y \"Z 0YAZ \"X >Y .Y3Y 3Z '\\ MZ )Z ;Z 2^ +Y ;Y " - "X Y 6Y /Y5Y $[ =` G^ !Z IZ M\\ #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[ B\\KXJ[" - " &X '\\JXK\\ H\\ 1Z ,U&V EY;Y /X ,Z 'V@jDV Gp +UDj?V >V<V +X GW )` $nEW KV /W7W " - "BW-X IW-X N`0W *YIZ 1YIY 0YHY 1YIY 0ZIY 1YIZ ?ZDZ 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)" - "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[ \"[ &Z &[ ![" - " #\\ #[ ![ G[@W IYBZ J]8] I\\7\\ H]8] I]8] I\\7\\ 2X 8Y*Z @Z \"U .k 5q N~o Mm 4l =X" - ".X 7^ 7Z3Z NZ %Z 6Z +XCX /W?W D[FT@S)W;W>X AZ :X \"Y JX (f &X )X ;Z3Z 2Z @Z !Z 7" - "XHZ ?Y !Z /Z CY1Y JZ1Z 2Y Y $Z Z HY JYHY ?Z/Y L[ %Z'\\ NZ MZ -[ Z(Z :Z \"Z 4Z@\\ 7Z 2YKX5XKY(YKZ7Z'[" - "$[ NZ2Z%[%[#Z2[ JZ :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X X ?Y ;[6[ (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X NX " - "0X1Z 8Y FXD`9` YD` -c 9XD` /aEX :XD] 6g 7t BX0Y LY)Y+X6Z6X)Z/Z NX)Y I} 2Y X Y 9_>W KY5Y #[ =c h >XD` " - "AT#X 5Y 6X0X LY'Y ?RCW ?~Y!X?X?X ;d 'r!~W KZ1Y =YHY 2\\ )Z /[$[%_GZG_#Z(Z+[;Z;Z-[%[ C\\JXI[ 'X (\\IXJ\\ " - " (Y d 5Z -W(X FY<Y .X ,[ (UAmDV Iq ,VDl@U >V=W +X HX )^ ,Y1Y HnEW KV 0X7W BW-W HW.X M^/X )" - "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#" - "Z(Z$Z(Z$Y'Y H[>Z @{ DY=b ;f -f -f ,e -f -f Ae7c ;e /b )c *c *c 'Y NX NX X E[ >XD` -c )c *b *c )c '\\ &bDX L" - "X0X GX0X GX0X GX0X KY)X KYE` ?Y*Y 8[4\\ K[3[ J\\4[ I[4\\ K[3[ 3X 8Z+Z AZ !U /m 6q N~o No 6o ?X.X 8_ " - "6Y3Z Z $Z 6Z +XCX 0X@X DZET>Q)W;W>W ?Y :X \"X IY 'b $X )X ;Z2Y 2Z @Z !Z 8YHZ ?Y " - "!Z 0[ CY1Y JZ1Z 5\\ \\ 'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ" - "2Z&[#Z#Z2[ JZ :Y 9Z(Z\"Z,Z1Z8XJX8Z%[D[ <Z?[ \"\\ 6X X ?Y <[4[ -l :YGd ,k 9eGY :h 5r 8eGY GYGe +Y NX 0X3" - "\\ 8Y FYGd=c!YGe 2h ;YGd 3eGX ;YG` 9m :t BY1Y LZ+Z+Y7[7Y*[1Z MY+Z J~ 2Y X Y <eAW KY5Y \"Z <f 'o CYFd D" - "Y(Y 5Y 6Y1Y MY'Z CUE\\ B~Y!Y@X@Y =h 0z\"~W KY0Y >ZHY 1\\ *Z /[#['^EZE^$Z(Z+[;Z;Z.[#Z C[IXH[ (X ([HXI[ (" - "Z $k 9Z .Y*Z FY=Y .X ,\\ *UAnCU J^CW -VCmAV ?W>V *X IX (a /Y1Y HnEW KV 0X7W BW.X HW.W La3X " - "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z(" - "Z#Z(Z$Z(Z$Y'Y GZ?Z ?| EY>c >l 4l 3l 2l 3l 4l Gl=h @k 5h /h /h /h )Y Y NX Y E[ ?XFd 1g .h /h /h /h )\\ )hHX " - "LY0X HY0X GX0X GX0Y LZ+Y KYGd AY*Y 9[EXD[ M[1[ L[1[ K[1[ M[1[ 4X 8Z+Y A[ !T /n 6q N~o q 8q @X.X 8` 7" - "Y3Y Z $Z 5Z +XCX 0X@X DYDT EW;W?X ?Y :X #Y IY %^ \"X )X <Z1Z 3Z @Z !Z 8XGZ ?Y !Z" - " 0Z BY2Z JY0Z 8_ _ *Z!Y DX LYFY @Z/Y M[ $Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZB\\ 5Z 2YJX7XJY(YJZ8Z([#[ NZ2Z&[" - "#[$Z2[ JZ :Y 9Z(Z\"Z-Z/Z9XJX9Z#ZDZ :Z@Z \"\\ 5X NX @Y =[1Z 1q <YIh 0o =hHY <l 7r 9hIY GYHg ,Y NX 0X4\\ " - "7Y FYIg@g#YHh 6l =YIh 7hHX ;YHa ;q <t BY1Y KY+Y*Y8\\8Y([3[ MY+Y I~ 2Y X Y =gCX KY6Z !Z <i -q CYHh F[*Y" - " 5Z 7Y1Y NZ&Y EWG` D~Y!Y@X@Y >k 5}\"~W KY0Z ?YGZ 1[ *Z /Z\"[(]CZD^%Z(Z+[;Z;Z.[#[ CYHXGY 'X 'YGXHY 'Z &o" - " ;Z /[,[ FZ?Y -X +\\ +UBoBU LZ>W -UBnAU >W@W *X JX 'c 1Y1Y HnEW KV /W7W BW.W GW/X Lc5W 'Y ," - "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#" - "Z(Z$Z(Z$Y'Y G[A[ ?} FY?] :p 8q 8q 7q 8q 8p LqAl Do 9l 3l 3l 3l +Y Y NX Y #i @XHh 5k 2l 3l 3k 2l +\\ +lKX KY0" - "X HY0X GX0X GX0Y KY,Z KYIh CZ,Z :ZCXC[ [/[ N[.Z MZ.[ [/[ 5X 7Y,Z AZ !U /o 7p M~n s :s AX.X 8` 7Z4Y Y" - " #Z 5Z +XCX 0W?X EYCT EW;W@X >Z ;X #Y HX #Z X *X ;Z1Z 3Z @Z !Z 9XFZ ?Y \"Z /Z " - "BY2Z KZ0[ <b a -[\"Y BX MYFY @Z0Z M[ $Z%[ Z MZ .Z MZ(Z :Z \"Z 4ZD] 4Z 2YJX7XJY(YJZ8Z([\"[ Z2Z&Z\"[$Z2" - "[ JZ :Y 9Z(Z!Z.Z/Z9WHW9Z\"ZF[ :[BZ \"\\ 4X NX @Y >[/Z 4t =YJj 3q >kJY >o 8r ;kJY GYJk .Y NX 0X5\\ 6Y FY" - "JiBi$YJk 8o ?YJj 9kJX ;YJc <r <t BY1Y KZ-Z)X8\\8Y'Z4[ LZ,Y I~ 2Y X Y ?jDX KY6Y Z ;k 1r CYIj G]-Z 5Z 7" - "Y1Y NZ&Z HYHb E~Y!Y@X@Y @n 8~P\"~W KY0Z ?YFY 0[ +Z 0[!Z)]BZB]&Z(Z+[;Z;Z.Z\"[ LQ GWGXFW HQ /X /Q*Q @WFXGW &Z" - " (q ;Z .[BVB[ DY@Z -X *] .UC^EXBU LX<W .VBWC[AU ?WAW )X KX %c 2Y1Y HnEW KV /W7W BW/X GW/W J" - "c7X 'Y ,YFY 4YFZ 3YFY 4YEY 3YFY 4ZFY AYBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\" - "&Z/YL[(Z(Z#Z(Z$Z(Z$Y'Y F[BZ >Z@d GY@\\ :t ;t <u ;t ;t ;t tDn Gr <o 6o 6o 6o ,Y Y NX Y &l @XIj 8o 5o 6n 6o 5o" - " -\\ ,nLW JY0X HY0X GX0X GX0Y KY,Y JYJj CY,Y :ZBXBZ!Z+Z Z,Z Z,Z!Z+Z 6X 7Z-Z BZ U 0q 7o M~n s ;u BX." - "X 9a 6Y5Z!Y \"Z 5Z +XCX C~d&YCT EW;W@W =[ <X #Y HY $Z X *X ;Z1Z 3Z @Z !Z :YFZ ?" - "Y \"Z 0Z AZ3Z KZ0[ 5Z \"[ ?e d 0Z\"Y AY YEZ AZ0Z MZ #Z%[ Z MZ /[ MZ(Z :Z \"Z 4ZE] 3Z 2YJY9XIY(YIZ9Z(Z![ " - "Z2Z'[!Z$Z2[ JZ :Y 9Z(Z!Z/[/Z:XHW9Z\"[H[ 8ZC[ \"[ 3X NX @Y ?[-Z 5v ?YKm 6r ?mKY ?q 9r <mKY GYKm /Y NX 0X" - "6[ 4Y FYKkEl%YKm ;r @YKl ;mKX ;YKd >t <t BY1Y JY-Y(Y9]9Y&Z5Z JY-Y H~ 2Y X Y @lFX JY6Y NY 9k 4s CYJl H" - "^.Y 4[ 8Y1Y NY$Y J[Ie G~Y!Y@X@Y Ap ;~R\"~W KY0Z @YEZ 0[ ,Z 0Z [*\\AZA\\&Z(Z+[;Z;Z/[![ NS GUFXEU HS 0X 0S,S @U" - "EXFU %Z )r ;Z -[G^G[ CZAY ,X )] /UC[>TAU NX;W )P9P =UAWAYAU >XDX )X LX HY 3Y1Y HnEW KV /W7W " - "AP9P 9W0X FW0X ?Y8W &Y -YEZ 5YEY 4ZFZ 5YEY 4ZEY 5YEY BZBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['YIZ8Y([!Z+Z![,Z![-" - "[![-[!Z$[1\\&[/XJZ(Z(Z#Z(Z$Z(Z$Y'Y EZCZ =Z;` HYA[ 8u <u =v <v =u <u!uGr Js =r 9r 9r 9r .Y Y NX Y (o AXJl :q " - "7q 9r 9q 7q .\\ -y IY0X HY0X GX0X GX0Y KZ-Y JYKl DY-Z ;ZAXAZ\"Y)Y!Z*Z\"Z*Z\"Y)Y 6X 7Z-Y BZ NT 0s 8o" - " L~m!u =w CX.X 9b 7Y5Y Y \"Z 5Z +XCX C~d&YCT EX<WAX <Z <X #X GY &^ \"X *X ;Z0Y 3Z" - " @Z !Y 9XEZ ?Y \"Z 0Z AZ3Y JZ/Z 5Z \"[ Ag g 4[\"X ?X YDY AZ0Z MZ #Z%[ Z MZ /[ MZ(Z :Z \"Z 4ZF] 2Z 2YIX9" - "XIY(YIZ9Z(Z Z Z2Z'[![%Z2[ JZ :Y 9Z(Z!Z/Z.Z:XHX:Z!ZHZ 6ZDZ \"\\ 3X NY AY @Z*Z 6w @YLo 9t @oLY At :r =oLY " - "GYLo 0Y NX 0X7[ 3Y FYLmGn&YLo =t AYLo >oLX ;YLe ?u <t BY1Y JY-Y(Y9]9X%[7Z IZ.Y H~ 2Y X Y AnGX JY7Z N" - "Z 9k 6t CYKn I^/Z 5\\ 8Y1Y Z$Z L\\Jg H~Y!Y@X@Y Br =~S\"~W LZ/Y @YDY /[ -Z 0Z NZ+\\@Z@\\'Z(Z*Z;Z;Z/[![ U GSEXDS" - " HU 1X 1U.U @SDXES $Z +t ;Z ,[JbJ[ AYBY +X (^ 2UCZ9QAU NW:W *Q:Q >VAW?XAU ?ZHY (X MX EX 4Y1Y HnE" - "W KV /W7W AQ:Q :W0W EW1X <X:X &Y -YDY 6ZEZ 5YDY 6ZEZ 5YDY 5YEZ CZBZ :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['YHZ" - "9Y(Z Z+Z Z-[![-[![-Z [$[3\\%[0XI[)Z(Z#Z(Z$Z(Z$Y'Y E[E[ =Z9^ HYBZ 6v =v >w =w >v =v\"vIt Lt >t ;t ;t ;t /Y Y N" - "X Y *r BXKn <s :t ;t ;s :t /\\ /{ IY0X HY0X GX0X GX0Y JY.Z JYLo FZ.Y :Y@X?Y$Y'Y#YIP5PIY\"Y.PIY$Y'Y 7X 6Z/Z" - " CZ NU 1u 8m K~m\"w ?^C] CX.X 9b 7Z6Y X \"Z 4Z +XCX C~d&XBT EX=XAW ;[ =X $Y GY (" - "b $X +X :Y/Z 4Z @Z \"Z :XDZ ?Y \"Y 0[ @Y4Z JZ/Z 5Z \"[ Dj j 8[\"X =X\"ZDY AZ0Z N[ #Z$[!Z MZ /Z L" - "Z(Z :Z \"Z 4ZG] 1Z 2YIX:YIY(YHZ:Z)[ [!Z2Z'Z [%Z2[ J[ ;Y 9Z(Z Z0Z-Z;XHX;Z NZJ[ 6[FZ \"\\ 2X MX AY AZ(Z 7x" - " AYMq ;u AqMY Bv ;r >qMY GYMp 0Y NX 0X8[ 2Y FYMoIp'YMq ?v BYMp ?qMX ;YMf ?u <t BY1Y JZ/Z(Y:^:Y$[9[ HY/Z H~ 2Y " - " X Y BpHX JY7Z MY ;o 9u CYLp J_0Y 4\\ 8Y1Y Y#Z M]Jh I~Y!Y@X@Y Ct ?~T\"~W LZ/Y AZDY .[ .Z 1[ NZ+[?Z?['Z" - "(Z*Z;Z;Z/Z NZ!W GQDXCQ HW 2X 2W0W @QCXDQ #Z ,u ;Z +[MfM[ ?YCY +X '_ 4UDZ'U W:W +R;R >U@W?XAU >j (X " - " NX CX 5Y1Y HnEW KV /W7W AR;R ;W1X EW1W :X<X %Y .ZDY 6YCY 5YDZ 7YCY 5YDZ 7ZDY DZAZ ;[ JZ !Z Z !Z >Z " - "!Z !Z \"Z :Z$Z'YHZ9Y)[ [-[ [.[ Z-Z NZ-Z [#[5\\$Z0XH[)Z(Z#Z(Z$Z(Z$Y'Y D[FZ <Z7] IYBY 5w >w ?x >x ?w >w#wKv Nu ?v" - " =v =v =v 0Y Y NX Y +s BXLp >u <v =v =u <v 0\\ 0{ HY0X HY0X GX0X GX0Y JZ/Y IYMp EY.Y ;Y?X?Y%Y%Y$YJR7RIY$" - "Y.RJY%Y%Y 8X 6Z/Y CZ MU 2v 8m K~m#y @[>\\ DX.X :c 7Z7Z!Y \"Z 4Z +XCX C~d&XBT DW=XB" - "X :[ >X $Y FY +f &X +X ;Z/Z 4Z AZ !Z ;YDZ ?YFP -Z?Q BZ ?Z5Z JZ/Z 5Z \"[ Gj Ii ;[\"X1Q,W\"YCZ BZ1" - "Z MZ \"Z$[!Z MZ /Z LZ(Z :Z \"Z 4ZH] 0Z 2YHX;XHY(YHZ:Z)Z N[!Z2Z([ NZ%Z2Z I[ ;Y 9Z(Z Z1Z,Z;XGW;Z N[L[ 4[H[ #\\" - " 1X MX AY BZ&Z 8^Ga AYN[H_ <cI\\ B`I[MY CaH_ <r ?`H[NY GYNr 1Y NX 0X9[ 1Y FYNqJp'YMq @aJa CYN[H_ A`I[MX " - ";YNg @`E[ <t BY1Y IY/Y&X:^:Y#Z:[ GY/Y G~ 2Y X Y JW5V B`M_JX IY8Z LY =r ;cL_ CYM^Na J`1Y 5^ 9Y1Y!Z\"Z ^K" - "j J~Y!Y@X@Y D_I` A~U\"~W LY.Y AYCZ .[ /Z 1Z MZ,\\?Z?\\(Z(Z*Z;Z<[/Z NZ\"Y ;X ;Y 3X 3Y2Y 3X EZ -hM[ ;Z *~Q >" - "YDY *X )b 6UDY%U V9W ,S<S >U@W>W@T =h 'X X AW 5Y1Y HnEW KV /X9X AS<S <W1W DW2X 9W<W $Y .YCZ 7Y" - "CY 6YBY 7YCY 6ZCY 7YCZ EZAZ ;[ JZ !Z Z !Z >Z !Z !Z \"Z :Z$Z'YGZ:Y)[ NZ-[ [.Z N[.Z NZ.[ NZ\"[7\\$[1XFZ)Z(Z#Z(" - "Z$Z(Z$Y'Y CZGZ ;Z6\\ IYCY 4^Ga ?^Ga @_Hb ?^Ga ?^Ga ?^Ga$^GaMaI`!bH\\ @aI` ?aI` ?aI` ?aI` 1Y Y NX Y ,u CXM^Nb" - " @aKa >aJa ?aJa ?aKa =`Ja 1\\ 0`Ic GY0X HY0X GX0X GX0Y IY0Z IYN[H_ FZ0Z <Y>X>Y&X#X%YJT9TIY&Y.TJY&X#X 8X 5Y0" - "Z CZ ;P4U 1w 9l J~m#z B[;[ EX.X :d 7Y7Y X )~Q #Z +XCX C~d&XBT DW=XCX 9\\ ?X $Y FY " - "-j (X +X ;Z/Z 4Z AZ \"Z :XCZ ?YM_ 5ZE^ IZ >Y6Z IZ0[ 5Z \"[ Jj Ci ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z " - "MZ 0[ LZ(Z :Z \"Z 4ZI] /Z 2YHX;XHY(YGZ;Z)Z N[!Z3[([ NZ%Z2Z H[ <Y 9Z(Z NZ2Z,Z<XFW;Z MZLZ 2ZHZ #\\ 0X MX AY C" - "Z$Z 9Y>^ BcB] >_?W C^CYNY C]A] 4Y /]Bc GYNYD^ 2Y NX 0X;\\ 0Y FYNXC\\KYD](YNYC] A]B^ DcB] C^CYNX ;YNZDQ A\\" - ";V 5Y .Y1Y IY/Y&Y;_;Y\"Z;Z FZ0Y $[ 2Y X Y M];\\ F]E[JX IY9[ LY >ZKf =]=V CYNYC] K`2Z 5^ 9Y1Y!Z\"Z!^JZM^" - " K~Y!Y@X@Y E]C^ CaHl\"~W LY.Z BYBY .\\ 0Z 1Z M[-[>Z>[(Z(Z*Z;Z<[0[ N[$[ <X <[ 4X 4[4[ 4X EZ ._KUHV ;Z )~ <Y" - "EY *X *e 8UDY$T!W:X .U=T ?U?W>W@U =f &X !X @W 5Y1Y HnEW KV /X9X AT=T =W2X DW2W 8W=X $Y .YBY 8ZC" - "Z 7YBY 8ZCZ 7YBY 8ZBY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YGZ:Y)[ NZ-Z MZ.Z N[/[ N[/[ NZ![9\\#[2YFZ)Z(Z#Z(Z" - "$Z(Z$Y'Y C[I[ ;Z5\\ JYCY 4X=^ @X=] @Y=] ?Y>^ @X=^ @X=^%X=l@\\\"_?W A]@\\ @]@\\ @^A\\ @^A\\ 1Y Y NX Y -w DXNY" - "C] A^C^ ?^C^ A^B] @^C^ ?^C^ 2\\ 1^C_ FY0X HY0X GX0X GX0Y IY0Y HcB] FY0Y ;X=X=Y(Y#Y'YJV;VIX&X.VJY(Y#Y 9W 4Z1" - "Z DZ =S4U 2y 9j I~l#{ BZ9Z EX.X :d 7Z8Y!Y *~R #Z +XCX C~d'YBT DX?XBW 7\\ @X $Y FY " - "/ZNVNZ *X ,X :Z/Z 4Z AZ #Z :XBZ ?o 9ZGc MZ =Z8[ HY0\\ 6Z \"[ Li >j C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!" - "Z MZ 0[ LZ(Z :Z \"Z 4ZJ] .Z 2YHX<YHY(YFY;Z)Z MZ!Z3[([ N[&Z3[ H] >Y 9Z(Z NZ2Z,Z<XFX<Z LZN[ 2[JZ \"[ /X LX B" - "Y DZ\"Z :U7\\ Ca>\\ @^:T C\\?b D\\=\\ 5Y 0\\>a Ga?\\ 2Y NX 0X<\\ /Y Fa@\\MX@[(b@\\ B]?\\ Da?] D\\?a ;b 1Z6" - "S 5Y .Y1Y IZ1Z&Y;_;X![=Z DY1Y #[ 2Y X Y `>` I\\B[KX IY:\\ LY ?ZDa ?\\7R Cb?\\ F[3Y 5_ 9Y1Y\"Z Y!]IYJ] L" - "~Y!Y@X@Y F\\?\\ D^Ai\"~W LY.Z CZBZ .\\ 1Z 1Z LZ.[=Z>[(Z(Z*Z;Z<[0[ N[%\\ <X <\\ 5X 5\\4\\ 5X EZ /^IUFT ;Z (" - "| ;YFY )X +h :TDY#U\"W:X /V?V ?U?W>XAU <c $X \"X ?X 6Y1Y HnEW KV .W9W @U>V ?W3X CW3X 8X>W #Y /Z" - "BZ 9YAY 8ZBZ 9YAY 8ZBZ 9YAY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YFZ;Y)Z MZ-Z MZ/[ MZ/[ N[/Z M[![;\\\"[3YE[*" - "Z(Z#Z(Z$Z(Z$Y'Y B[JZ :Z4[ JYCX 3U8\\ @U8\\ AV8\\ @U7\\ AU7[ @U8\\%U8h=\\$]9T B\\=\\ B\\=\\ B\\=\\ B\\<[ 2Y Y " - "NX Y .x Da?\\ C]?] A]?] B\\?] B]?] A]?] 3\\ 2]?] FY0X HY0X GX0X GX0Y IZ1Y Ha?] GY1Z <X<X<X(X!X'XJX=XJY(X.X" - "JX(X!X 9W 4Z1Y >~d W5T 2{ 9i H~k$} DZ7Z FX.X :d 7Z9Z!X )~R #Z 0~d&XBT DX?XCX 6\\ " - " =Y EY 0ZMVMZ +X ,X :Z/Z 4Z B[ %\\ :XBZ ?q ;YHg Z <Z:[ GZ1\\ 6Z \"[ i M~c Nj G\\!W9eIVBX%Y@Y CZ3[ M" - "[ \"Z#Z!Z MZ 0Z KZ(Z :Z \"Z 4ZK] -Z 2YGX=XGY(YFZ<Z*[ MZ!Z3[(Z M[&Z3[ H^ ?Y 9Z(Z NZ3Z*Z=XFX=Z Kf 0[L[ #\\ /X " - " LX BY JS4[ C`<\\ A\\5Q D[;` E[9Z 5Y 1\\<` G`<Z 2Y NX 0X=\\ .Y F_=[MV=[)`<[ D\\<\\ E`<[ E[;_ ;` 0Z3Q 5" - "Y .Y1Y HY1Y%Y<`<Y [?[ DZ2Y $[ 1Y X Y !cBc J[?YLX HY<] JX @Y?_ @[ '`<[ EZ4Z 5` :Y1Y\"Z Z#\\GYI\\ EZ:Z IY@" - "X@Y FZ;[ E]>\\ 0Z 6Y.Z CYAZ -\\ 2Z 1Z LZ.[=Z=[)Z(Z*Z;Z<Z/Z LZ&\\ ;X ;\\ 6X 6\\2\\ 6X EZ /\\GUCQ ;Z 'z 9YGY" - " )X -ZN_ ;TDX\"U\"W;Y 0W@W ?T>W>X@T ;a #X #X =W 6Y1Y GmEW KV .X;X @W@W @W3W BW4X 6W?X #Y /Y@Y :" - "ZAY 8Y@Y 9YAZ 9Y@Y 9YAZ GZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YFZ;Y)Z M[/[ MZ/[ MZ/Z LZ/Z M[ [=\\!Z3YD[*Z(Z#Z" - "(Z$Z(Z$Y'Y AZKZ 9Z4[ JYDY 3R3[ AR3[ BS3Z @S4[ AS4[ AR3[&R3e:[&]6R C\\:[ D\\:[ D\\:[ D\\:[ 3Y Y NX Y /_B] E_<" - "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y ;X<X<X)X NX)YKZ?ZJX(X/ZKX)X" - " NX ;X 3Y2Z >~d#Z6U 3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X *~R \"Z 0~d&YCT CXAXBW 5] " - " >Y EY 2ZKVKZ -X ,X :Z/Z 4Z BZ &] :XAZ ?s =YJk #[ ;[=[ FZ1\\ 6Z \"[ #j L~d Ki J\\!X:hKVAW%Y@Y CZ5\\ L" - "[ \"Z#Z!Z MZ 0Z KZ(Z :Z \"Z 4ZL] ,Z 2YGX=XGY(YEZ=Z*[ M[\"Z4['Z LZ&Z4[ F` BY 9Z(Z MZ4Z*Z=XEW=Z Jd .ZLZ #\\ .X" - " LX BY JQ1[ D_:[ B\\ ([9_ F[7Z 6Y 1[:_ G^9Z 3Y NX 0X>\\ -Y F^;b;Z)_:Z D[:\\ F_:[ G[9^ ;_ /Y EY .Y1Y " - "HY2Z$Y=a=Y NZ@[ BY3Z %[ 0Y X Y \"eCd L[>YLX HY>^ IY AY=] @Z &_:Z DY4Y 5a :Y1Y\"Z Z$\\GYG\\ EY9Y IY@X@Y G" - "Z9[ G\\;[ 0Y 5Y.Z DZ@Y ,\\ 3Z 1Z LZ.Z<Z=[)Z(Z*Z;Z<Z/Z LZ'\\ :X :\\ 7X 7\\0\\ 7X EZ 0\\FU -Z &x 8YHY (X -YK" - "_ >UDX!T\"X<Y 1XAX ?T>W>X@U :] !X $X <W 6Y1Y GmEW KV .Y=X ?XAX AW4X BW4W 5W@X \"Y 0Z@Y :Y@Z 9Y@" - "Y :Z@Y 9Y@Z ;Z@Y HZ?Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ M[/[ M[0Z LZ/Z LZ/Z M[ N[?\\ Z3XBZ*Z(Z#Z(Z$Z(Z" - "$Y'Y @ZM[ 9Z3[ KYDY 3P0Z AP0Z BQ0Z AQ0Z BP0Z AP0Z&P0b7Z'\\2P CZ7Z DZ7Z DZ7Z DZ7Z 3Y Y NX Y 0]<Z E^:Z D[9[ C[" - ":\\ E\\:[ D[9[ C[:\\ 4\\ 3[9[ GY0X HY0X GX0X GX0Y HZ3Y G_:[ GY2Y <X;X;X*X NX)XJ[A\\JX*X/[JX*X NX ;X 3Z3Z " - " >~d&^7U 4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X )~R \"Z NW?W BYCT CYBXCX 6_ ?Y EZ 5ZI" - "VIZ /X ,X :Z.Y 4Z C[ )_ :YAZ ?t >YKn %Z 9\\A\\ EZ1\\ 6Z \"[ &j I~d Hi N\\ W:jLVAW&Z@Z DZ8^ KZ !Z#[\"Z " - " MZ 0Z KZ(Z :Z \"Z 4ZM] +Z 2YGY?XFY(YEZ=Z*Z L[\"Z4['Z LZ&Z4[ Fc EY 9Z(Z MZ5Z)Z>XDW=Z Ic .[NZ #\\ -X KX CY " - " )Z D^8[ D\\ '[8^ FZ5Z 7Y 2[8^ G]8Z 3Y NX 0X?[ +Y F]9`9Y)^9Z E[8[ F^8Z GZ8^ ;^ .Y EY .Y1Y GY3Y#Y=WNX=Y M" - "ZAZ AY3Y %[ /Y X Y #gEf N[<YMX HYBb IY BY;] BZ %^8Z DY5Y 5b ;Y1Y#Z NZ$[FYF[ EY9Y IY@X@Y HZ8[ H\\9[ 1Y 5Y" - ".Z DZ@Z ,\\ 4Z 2[ LZ.Z<Z<Z)Z(Z*[<Z<Z/Z LZ(\\ 9X 9\\ 8X 8\\.\\ 8X EZ 1\\EU -Z %^E] EhIg 6X .YI_ ?UEX T!W=" - "Z 2YBY @U>W>W?U 7W <~d BX ;W 6Y1Y GmEW KV -X=X ?YBY BW4W AW5X 5W@W !Y 0Y?Z ;Y?Y :Z@Z ;Y?Y :Z?Y ;Y" - "?Y HZ?Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ LZ/[ M[0Z LZ/Z LZ0[ LZ M[A\\ NZ4XAZ*Z(Z#Z(Z$Z(Z$Y'Y @[NZ 8Z3" - "[ KYDY AZ !Y Y Z !Z !Z 5`5Z([ %Z5Z FZ5Z FZ5Z FZ5Z 4Y Y NX Y 1\\:[ F]8Z F[7[ E[8[ E[8[ E[8[ E[8[ 4\\ 4[9\\" - " GY0X HY0X GX0X GX0Y GY4Z G^8Z GZ4Z <X;X:W+X LX*WH[C\\IX*X0[HW+X LX <X 2Y4Z =~d(`7T 4~Q 9e E~i%~R GY3" - "Y GX.X ;YMZ 7Z;Z!X *~R !Z X@X BZDT BXCYDX 6` ?Y DY 7[HVH[ 1X -X 9Z.Y 4Z D[ 7" - "m 9X@Z ?v AZLp &Z 8^H_ DZ1\\ 6Z \"[ (i F~d Ei #\\ NW;lMV@W'Y>Y D~P JZ !Z#[\"~Q Dy Z K~] :Z \"Z 4ZN] *Z 2YFX?XF" - "Y(YDZ>Z*Z L[\"Z5\\([ LZ&Z5\\ Eg JY 9Z(Z MZ5Z)Z>XDX>Z Ib ,f $\\ ,X KX CY (Y D]6Z D[ '[7^ GZ4Z 7Y 2Z6] " - "G]7Z 4Y NX 0X@[ *Y F]8^8Z*]7Z FZ6[ G]6Z I[7] ;] -X DY .Y1Y GY3Y#Y=WNX=X L[CZ ?Y4Y &[ .X NX Y $iGh Z:XNX" - " GYHg HY CY8\\ CY $]7Z DY6Y 4b ;Y1Y#Z MZ&[EYE[ FY9Y IY@X@Y HZ7[ I[7[ 2Y 5~V DY>Y +\\ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z<Z/" - "ZIuIZ)\\ 8X 8\\ 9X 9\\,\\ 9X EZ 1[DU -Z $Z@[ EhJh 6X /YF_ ATDX U\"X?[ 3ZCZ @U>W>W?U K~d CX ;X " - " 6Y1Y FlEW KV -Y?Y ?ZCZ CW5X AW5W 5XAX !Y 0Y>Y <Z?Z ;Y>Y <Z?Z ;Y>Y ;Y?Z JZ>~Q3[ I~Q G~Q F~Q G~Q 5Z !Z !Z " - "\"Z :Z#Z(YDZ=Y*[ LZ/Z L[0Z L[0Z LZ0[ LZ L[C\\ N[5X@Z*Z(Z#Z(Z$Z(Z$Y'Y ?e 7Z3[ KYDY @Y Y !Z Y Y Y 4_4Y)[ %Z3" - "Y GZ3Y FZ4Y FZ4Y 4Y Y NX Y 1[8Z F\\7Z F[7[ EZ6[ G[6[ G[6Z EZ6[ <Z9^ HY0X HY0X GX0X GX0Y GY4Y F]6Z GY4Y " - " ;W:X:X,X LX+XG[E\\GW*W0[GX,X LX <X 2Z5Z =~d(`8U 4~R 9c D~h%~T HX2Y GX.X <ZLY 6Y;Z!X *~" - "R !Z X@X BZDT BZGZCW 6b @Y DY 8ZFVFZ 2X -X 9Z.Y 4Z DZ 7l 8X?Z ?w BZMr ([ 7s C[3] 6Z \"[ +i C~d" - " Cj '\\ NW;nNV@W(Z>Y D~ IZ !Z#[\"~Q Dy![ K~] :Z \"Z 4h )Z 2YFX@YFY(YDZ>Z*Z KZ\"Z5\\([ LZ&Z6\\ Ck Y 9Z(Z LZ6Z(" - "Z?XDX?Z G` *d #[ +X KX CY 'Y E]6[ F[ &Z5] GY2Y 7Y 3Z4\\ G\\6Z 4Y NX 0XA[ )Y F\\7]6Y*\\5Y G[5Z G\\5Z I" - "Z5\\ ;] -X DY .Y1Y GZ5Z#Y>XMW>Y K[E[ ?Y5Y &[ .Y NX Y $XIZHZIY!Z:XNX GYHf GY DY6[ CY $\\5Y CX6Y 5c ;Y1Y#" - "Z MZ&[EYDZ FY9Y IY@X@Y IZ5Z IZ5Z 2Y 5~V EZ>Y *[ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z=[0[IuIZ*\\ 7X 7\\ :X :\\*\\ :X L[" - "CU -Z %Z>Z EiKh 6X /XC^ BTDX U\"YA\\ 4ZCZ N~d &U>W?X>T K~d EY :W 5Y1Y EkEW KV ,YAY =ZCZ DW6X @W6" - "X 5W@W 'Z>Y <Y=Y <Z>Z =Y=Y ;Y>Z =Z>Y JZ>~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z :Z#[)YDZ=Y*[ LZ/Z KZ0Z L[1[ LZ0[ L" - "Z K[E\\ M[6Y@Z*Z(Z#Z(Z$Z(Z$Y'Y >d 7Z2Z KYDY @Y Y Y NY Y !Y 4^3Z*Z $Z3Z HZ3Z HZ3Z HZ2Y 5Y Y NX Y 2[6Z G" - "\\6Y FZ5[ G[5Z GZ5[ GZ5[ G[5Z =[:_ HY0X HY0X GX0X GX0Y GZ5Y F\\5Z GY5Z <X:X:X,W JW+XF[G\\FX,X1[FX,W JW <X " - "2Z5Y <~d'UNY9U 5~T H[LaM[!~g&~V JY1X GX.X <ZLZ 7Y;Y X Z 3Z W?X AZET A\\M\\CX 7d " - " BZ DY 8XDVDX 2X -X 9Z.Y 4Z E[ 7j 7Y?Z ?x CZNt )Z 5p @Z3] 6Z \"[ .i @~d @i *\\ MW<^Ib@W(Y=Z E| GZ !Z" - "\"Z\"~Q Dy![ K~] :Z \"Z 4f 'Z 2YEXAXEY(YCZ?Z*Z KZ\"Z6\\'[ LZ&Z8] An $Y 9Z(Z LZ7Z'Z?XDX?Z F_ *c #\\ +X JX DY " - " 'Y E\\4Z FZ %Z4\\ HZ1Y 8Y 3Z4\\ G[4Y 4Y NX 0XC\\ (Y F[6]6Y*[4Y GZ4[ H\\4Z JY4\\ ;\\ ,X DY .Y1Y FY5Y!Y?" - "XMX?Y JZF[ >Z6Y &[ .Y NX Y %WEYJYEX#Z8a GYHe FY DX4[ DY $\\5Y CY8Z 5d <Y1Y$Z LZ'[DYD[ GY9Y IY@X@Y IY4Z J" - "[5[ 3Y 6~W EY=Z *[ 6Z 2Z KZ/Z;Z<[*Z(Z)Z<Z=Z/[IuI[,\\ 6X 6\\ ;X ;\\(\\ ;X LZBU -Z %Y<Z FjMi 6X 0X@] CTD" - "W NU!ZE^ 5ZCZ M~d &T=W@X=T K~d FY :X 5Y1Y EkEW 3Z CV +ZEZ ;ZCZ EW6W ?W7XA]\"XAX 'Y=Z =Y=Y <Y<Y =Y=" - "Y <Z=Y =Y=Z KY=~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z L[1[ L[1[ LZ J[G\\ L[7Y?Z*Z(Z#Z(Z$Z(Z$" - "Y'Y >c 6Z2Z KYDY ?Y X NX NY Y Y 4\\1Y+[ %Z1Y HY1Y HY1Y HY1Y 5Y Y NX Y 3[5Z G[5Z HZ3Z GZ4[ HZ4Z HZ3Z GZ" - "4[ >Z9` IY0X HY0X GX0X GX0Y FY6Z F\\4Z GY6Y ;W9X9W-X JX,WD[I\\DW,W1[DW-X JX =X 1Y6Z <~d'RKY:U 5~U J" - "~T$~g'~X KY1X GX.X <YKZ 7Z<Y W NZ 3Y NW?W @\\GT @jCW 7f CZ DY 7VCVCV 1X .X " - "8Z.Y 4Z F[ 6h 5X>Z ?y DgF` *Z 2k >Z4^ 6Z \"[ 1j >~d =i -[ LW=\\C_?W)Y<Y Ez EZ !Z\"Z\"~Q Dy![ K~] :Z \"Z 4e &Z" - " 2YEXAXEY(YCZ?Z*Z KZ\"Z8^'[ L['Z:_ @p 'Y 9Z(Z KZ8Z'Z@XBW?Z F^ (b $\\ *X JX DY &X E[2Y FZ &Z3\\ HY0Y 8Y" - " 3Y2[ G[4Y 4Y NX 0XD\\ 'Y F[5[5Y*[4Y HZ2Z H[3Z KZ3[ ;[ ,Y DY .Y1Y FY5Y!Y?WLX?Y J[GZ <Y7Z '[ -Y NX Z 'WC" - "YKXBV#Z8` FYHc +YCY EY4[ DY $[4Z CX8Y 5e <Y1Y$Z KZ([DYCZ GY9Y IY@X@Y IY3Z KZ3Z 3Y 6~W EY<Y )[ 7Z 2Z KZ/Z;Z;Z*Z(" - "Z)[=Z=Z/[IuI[-\\ 5X 5\\ <X <\\&\\ <X LZBU -Z &Y:Y FjNj 6X 0X?] EUEX NU!s 6ZCZ L~d &T=WAY=T K~d GX" - " 9Y 5Y1Y DjEW 3Z CV *]M] 9ZCZ FW7X5X3W7WCc%XBX5Y JY<Y >Z=Z =Y<Y >Z=Z =Y<Y >Z=Z LZ=~Q3Z H~Q G~Q F~Q G~Q" - " 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z KZ1[ L[1Z KZ I[I\\ K[8Y>[+Z(Z#Z(Z$Z(Z$Y'Y =a 5Z2Z KYDY ?Y Y X MX Y Y" - " 4\\1Y+Z $Y0Y IZ1Y IZ1Y IZ0X 5Y Y NX Y 3Z3Y GZ3Y HZ3Z HZ2Z IZ2Z IZ3Z GZ3Z >Z:a IY0X HY0X GX0X GX0Y FZ7Y E[" - "3Z GY6Y ;W9X9W-W HW,WC[K\\CW,W2[CW-W HW =X 1Z7Z <~d NX:U 5~V M~X%~e&~Y LX0Y HX.X =ZJY 6Y=Z W " - " NZ 3Y X@X ?]IT ?hCW 7h2X ;Y CY 7TAVAT 1X .X 8Z.Y 4Z G\\ 6g 5X=Z ?X?a EeB^ +Z /f ;[5" - "^ 4i ;~d :i 1[ LW<Z?]?W*Z<Z Fx CZ !Z\"Z\"~Q Dy![ K~] :Z \"Z 4e &Z 2YEXBYEY(YBZ@Z*Z KZ\"Z9^&[ L['[Ad >r *Y " - "9Z(Z KZ8Z'Z@XBX@Y D\\ &` $\\ )X JX DY &X E[2Z HZ %Z3\\ IZ/X 8Y 4Z2[ GZ3Y 4Y NX 0XE\\ &Y FZ4[5Y*[4Z IZ" - "2Z H[2Y KY2[ ;[ +X DY .Y1Y FZ7Z!Y?WLX?X H[IZ ;Y7Y '[ ,Y NX NY *Q NV@WLW?U#Z8` FYHd .^FY EX2[ DX $[3Y CX8Y" - " 5YMY <Y1Y$Z KZ(ZCYCZ GY9Y IY@X@Y JY2Z L[3Z 3Y 6~W FZ<Z )[ 8Z 2Z KZ/Z;Z;Z*Z(Z)[=Z>[/[IuI[.\\ 4X 4\\ =X =\\$\\" - " =X MZAU -Z &X8Y G~W 6X 0W<\\ FUEX MT iNW 8[D[ K~d &T=WE\\<T K~d HX NQ<Y 4Y1Y CiEW 3Z CV )k 7" - "ZC[ HW7W5Y3W8XFh>Q<YAW5Z KZ<Z ?Y;Y >Z<Z ?Y;Y >Z<Z ?Z<Y LZ=~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YBZ?Y*Z KZ/" - "Z KZ0Z KZ1[ L[1Z KZ H[K\\ J[8X=[+Z(Z#Z(Z$Z(Z$Y'Y <` 5Z2Z KYDZ ?X Y Y NX NX NX 4[/Y,Z $Y/Y JY/Y JY/Y JY/Y " - "6Y Y NX Y 3Z3Z HZ3Y IZ1Z IZ2Z IZ2Z JZ1Z IZ2Z ?Z:b IY0X HY0X GX0X GX0Y EY8Z E[2Y GZ8Z ;W9X9X.W HW-XB[M" - "\\BW,W3[BX.W HW =X 0Y8Z ;~d NY;U 6~X!~[%~c&~Z LX0Y HX.X =ZJZ 7Y=Y N~l 4Z 3Y X@X ?`L" - "T >eBX<U\"[M\\4Y ;Y CZ 7Q?V?Q 0X .X 8Y-Z 5Z H\\ 5j 9Y=Z ?T9_ Ec>] ,Z 1j <[7_ 7i 8~d 7i 5[ KW=Z=" - "\\?W*Y:Y F{ FZ !Z\"Z\"~Q Dy![1j&~] :Z \"Z 4e &Z 2YDXCXDY(YBZ@Z*Z KZ\"Z<a&Z K['} <s ,Y 9Z(Z KZ9Z%ZAXBXAZ E] &_ $" - "\\ (X JY EY &Y F[2Z HZ %Y1[ IY.Y 9Y 4Z1Z GZ3Z 5Y NX 0XF\\ %Y FZ4Z3Y+Z2Y IZ1Z I[2Z LY1Z ;[ +X DY .Y1Y " - "EY7Y NX@XKW@Y G[K[ :Y8Y ([ ,Z NX NY /[(R NU?XNW=U%Z7_ EYHg 3bHY FY1Z DX $Z2Y CY:Y 5ZMZ =Y1Y$Z KZ)[CYBY GY9Y" - " IY@X@Y JY1Y LZ1Z 4Y 6~W FY;Z *[ 7Z 2Z KZ/Z;Z;Z*Z(Z(Z=Z>[/[IuI[/\\ 3X 3\\ >X >\\\"\\ >X MZAU -Z 'X6X 5c " - "%X 1X;\\ GUEX MT NgMW 9[D[ J~d &T=m;T K~d In 4TA[ 4Y1Y BhEW 3Z DX )i 5[D[ IX9W5Z3W8WFj?TA[BX5Z KY" - ";Z @Z;Z ?Y:Y @Z;Z ?Z;Y ?Y;Z NZ<~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YAY?Y*Z KZ/Z KZ1[ KZ1[ L[1Z KZ G[M\\ IZ8" - "X<[+Z(Z#Z(Z$Z(Z$Y'Y <_ 4Z2Z KYD[ @X NX Y NY X NX 3Z/Y-Z $Z/Y KZ/Y KZ/Y KZ/Y 6Y Y NX Y 4Z2Z HZ3Y IZ1Z I" - "Z1Z JY1Z JZ1Z IZ1Z @Z;XNZ JY0X HY0X GX0X GX0Y EY8Y D[2Z GY8Y ;X9X8W.W HW-W@hAW-X4[@W.W:[:W =X 0Z9Z I" - "[ 7Y<U 6~Y\"~^'~c'~\\ MX/X HX.X =YIZ 7Z>Y ~m 4Z 3Y W?X >g =cAW?]'[K\\5Y ;Y CZ %V M" - "X /X 7Y-Z 5Z H[ 4l ;X<Z ?Q4^ Fb<] .[ 3o ?[7_ :i 5j 9[ JW=Y;[?W+Z:Y F~ IZ !Z\"Z\"~Q Dy![2l'~] :Z " - "\"Z 4f 'Z 2YDXCXDY(YAZAZ*Z KZ\"~%Z K['| 9s .Y 9Z(Z JZ:Z%ZAXBXAZ E] %] #[ 'X IX EY &Y FZ0Y HY %Z1[ IY.Y" - " 9Y 4Y0Z GZ2Y 5Y NX 0XG[ #Y FZ4Z3Y+Z2Y JZ0Z IZ0Y MZ1Z ;Z *Y EY .Y1Y EY8Z NYAXKXAY FZL[ 9Y9Y ([ +Y MX NZ 4b," - "S U=`=U%Z6^ EYHi 6dIY FY1Z DY %Z2Y BX:Y 5ZLY =Y1Y%[ KZ)ZBYBZ HY9Y IY@X@Y JY1Z MZ1Z 4Y 6~W GZ:Y +\\ 7Z 2Z KZ/Z" - ";Z;Z*Z(Z([>Z>Z.[IuI[0\\ 2X 2\\ ?X ?\\ \\ ?X MY@U 8y ;X6X 4a $X 1X9[ HUEX MT MeLW :[D[ I~d &T=l:T " - "K~d Io 5m 3Y1Y AgEW 3Z Nl 2g 3[D[%lDX5Z>mDXFk@mAW5[ LZ:Y @Y:Z ?Y:Y @Z:Y ?Y:Z AZ:Y NZ<~Q3Z H~Q G~Q F~Q G" - "~Q 5Z !Z !Z \"Z Ew5[)YAZ@Y*Z KZ/Z KZ1[ KZ1[ L[1Z K[ Gh HZ9X;[+Z(Z#Z(Z$Z(Z$Y'Y ;] 3Z2Z KYC[ AX NX Y NY Y X" - " 3Y.Y-Z $Y.Y KY.Y KY.Y KY.Y 6Y Y NX Y 4Z1Y HY2Y IZ1Z IY0Z KZ0Z KZ1Z IY0Z @Y;XMZ JY0X HY0X GX0X GX0Y DY9Y D" - "Z0Y GY9Z ;W8X8W.W HW-W?f?W.W4[?W.W:[:W =X 0Z9Y HZ 5X<U 6~Z$~`'~a&~\\ NY/X HX.X =YHY 7Z?Z ~m " - " 4Z 3Y W?W <i >_@XAa*[I\\6Y ;Y CZ %V MX /X 7Y-Z 5Z I[ 3n >X;Z ] G`9\\ .Z 4s @[9` " - " =i /i ;Z IV=Y9Z>V+Z:Z G~P JZ !Z\"Z\"~Q Dy!Z1l'~] :Z \"Z 4g (Z 2YDYEXCY(YAZAZ*Z KZ\"}$Z K['z 5r /Y 9Z(Z JZ;Z" - "$ZAW@WAZ F_ %\\ $[ &X IX EY &Y FZ0Y IZ %Y/Z IY.Y 9Y 4Y0Z GY1Y 5Y NX 0XH[ \"Y FY3Z3Y+Z2Y JZ0Z IZ0Y MY0" - "Z ;Z *Z FY .Y1Y DY9Y MYAWJXAY F[MZ 8Z:Y )[ +Z MX N[ 7g1U U<^;U&Z6^ EYHj 9gJY FX/Y CY &Z2Y BY<Z 6ZKZ >Y1Y%Z" - " J[*ZBYBZ HY9Y IY@X@Y KY0Z MY/Y 4Y 6~W GZ:Z ,[ 6Z 2Z KZ/Z;Z;Z*Z(Z([>Z?[.ZHuI[1\\ 1X 1\\ @X @\\ M\\ @X NZ" - "@U 8y ;W4X 5` #X 1X8Z HUEX MT LbJW ;ZC[ H~d &T=j8U L~d Io 5l 2Y1Y @fEW 3Z Nl 0c 0[CZ&lDW5[>mEXE\\N^" - "AlAX6\\ LZ:Z AY9Y @Z:Z AY9Y @Z:Z AY9Z!Z;~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z K" - "[ Ff GZ:X:[+Z(Z#Z(Z$Z(Z$Y'Y :\\ 3Z2Z KYC\\ BY X NX NY Y X 3Y-X-Y #Y-X KY-X KY-X KY-X 6Y Y NX Y 5Z0Y HY" - "2Y IY/Y JZ0Z KZ0Z KY/Z KZ/Y AZ;WKY JY0X HY0X GX0X GX0Y DY:Z DZ0Y FY:Y :WK~KW.WK}KW-W>d>W.W5[>W.W:[:W =X /" - "Y:Z IZ 4Y=T 6~[%~b'~_%~\\ NY/X HX.X >ZHY 6Y?Y N~m 4Z 3Y !X@X ;l @[>WBe,ZG\\7Y ;Y" - " CZ %V ;~c LX 7Y-Z 5Z J\\ 2n @Y;Z N\\ G`8\\ /Z 5u A\\<b ?i *i ?Z IW=X8Z>V+Y8Y G~R LZ !Z\"Z\"~Q" - " Dy![2l'~] :Z \"Z 4h )Z 2YCXEXCY(Y@ZBZ*Z KZ\"|#Z K['x 0q 1Y 9Z(Z IZ<Z$ZBX@XBY F` %[ $\\ &X IX EY &Y FZ" - "0Z JZ %Y/Z JY,X 9Y 5Z0Z GY1Y 5Y NX 0XI[ !Y FY3Z3Y+Y1Y JZ/Y IZ0Y MY/Y ;Z *[ GY .Y1Y DY9Y MYBXIWBY Dg 7Y;Z *[ +" - "[ MX M[ :l6W T:\\:U&Y5] DYHk :hKY GY/Z DZ 'Z2Y BY<Y 5ZKZ >Y1Y%Z IZ*YAYBZ HY9Y IY@X@Y KY/Y MY/Y 4Y 6~W GY9Z " - "-[ 5Z 2[ LZ/Z;Z;Z*Z(Z'[?Z?[.[IuI[2~n BX B~n AX A~m AX NZ@U 8y <X4X 4_ #X 1X7Z IUEX MT J^HW <ZCZ F~d &T=" - "g5T -X ,o 5k 1Y1Y >dEW 3Z Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6] LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z H~Q " - "G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z KZ Ee FZ;Y:[+Z(Z#Z(Z$Z(Z$Y'Y :[ 2Z2Z KYB\\ CY X NX" - " NY Y Y 4Y-Y.Y #Y-X KY-X KY-Y LY-Y 7Y Y NX Y 5Z0Z IY2Y JZ/Z KZ/Y KY/Z KY/Z KZ/Y#~d$Z<WJY JY0X HY0X GX0X G" - "X0Y DZ;Y CZ0Y FY:Y :WK~KW/WJ}JW.W=b=W.W6[=W/W9[9W >X /Z;Z JZ 2X>U 6~\\'~c&~^$~Z MY/X HX.X >YGZ 7Z@Y " - "N~m 4Z 3Y !X@X :n 'WBg.ZE\\8X :Y CZ %V <~e NX 6Y-Y 4Z K\\ #a AX:Z M\\ H_6[ 0Z" - " 6aI` A]?c ?f $f ?Z IW>Y7Y>V,Z8Z HZ8` MZ !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZN] *Z 2YCXFYCY(Y@ZBZ*Z KZ\"{\"Z " - "K['v +o 2Y 9Z(Z IZ<Z#YBX@XCZ Fa %Z %\\ %X HX FY 6i FZ0Z JZ %Y/Z JY,X 9Y 5Z/Y GY1Y 5Y NX 0XK\\ Y FY3Z" - "3Y+Y1Y JY.Y IY/Z NY/Y ;Z *\\ HY .Y1Y DZ;Z LXBXIWBY Ce 6Y;Y )[ -\\ LX L\\ >q:X !U:[9U&Y5] DY?d =jLX FY/Z C[ " - ")Y1Y AX=Z 6ZIY >Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ/Z 5Y 5Y-Y HZ8Y .[ 4Z 1Z LZ/Z;Z;Z*Z(Z'[?Z@[-[ L[3~o BX B~o BX" - " B~o BX NZ@U 8y <X4X 4^ \"X 1X6Y IUEX MT GW *ZCZ E~d &T=g5T -X ,o 5i /Y1Y <bEW 3Z Nl *W 'ZCZ(l", - "EW6]>mFXDS?YBi?W5] CY 4Z8Y BY7Y BZ8Z CY7Y AY8Z CZ8Y!Y:Z <Z HZ !Z Z !Z >Z !Z !Z \"Z Ew5[)Y?ZBY*Z KZ/Z KZ1[ KZ" - "1[ L[1Z KZ Dc E[=Y9[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z2Z KYB^ &i 0i 1i /i 0i 0i Ej-Y/Z $Z-Y MZ-Y MZ-Y LY-Y 7Y Y NX Y 5Y/" - "Z IY1X JZ/Z KZ/Z LY.Y LZ/Z KZ/Z$~d$Z=WIZ KY0X HY0X GX0X GX0Y CY<Z CY/Z GZ<Z :WK~KW/WJ}JW.W<`<W.W7[<W/W9[9W " - ">X .Y;Y JZ 1Y?U 6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m 4Z 3Y !W?X 9p +XCi0ZC\\9X " - " :Y CZ %V <~e NX 6Z.Y 4Z L\\ M^ CY:Z L[ H^4Z 0Z 7^A^ C_Ce ?c Mc @Z HW>X6Y>V,Y7Z HZ5^ NZ !Z\"" - "Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZM] +Z 2YBXGXBY(Y?ZCZ*Z KZ\"z![ LZ&w 'k 3Y 9Z(Z IZ=Z\"ZCX@XCZ Gc &Z &\\ $X HX FY " - " >q FY.Y JY $Y/Z JY,X 9Y 5Y.Y GY1Y 5Y NX 0XL\\ NY FY3Z3Y+Y1Y JY.Z JY/Z NY/Y ;Y (^ KY .Y1Y CY;Y KYCXIXCY " - "Bc 4Y<Y *[ 2a KX La Du?Z !U9Z8T'Z5] DY9^ >\\IYMX FY/Z B\\ +Y1Y AY>Y 5ZIZ ?Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ" - "/Z 5Y 5Y-Y HZ8Z 0\\ 4Z 1Z LZ/Z;Z;Z*Z(Z&[@Z@[-[ L[4~p BX B~o BX B~p CX NY?U 8y <W2W 3] \"X 1Y7Y IUEX MT " - " JZCZ 8X &T=WIZ6T -X ,o 3e -Y1Y :`EW 3Z Nl (ZCZ)lFW5UNV>mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7" - "Y CY7Z#Z:Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#[)Y?ZBY*Z KZ/Z KZ0Z KZ1[ L[1Z KZ Ca D[>Y8[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ " - "KYA^ /q 9r 9q 7q 8q 9r Mq,Y/Z $Y,Y MY,Y MY,Y MZ-Y 7Y Y NX Y 5Y.Y IY1X JZ/Z KY.Z LY.Y LZ/Z KY.Z$~d$Y=XIZ KY0X" - " HY0X GX0X GX0Y CY<Y BY/Z FY<Y 9WK~KW/WJ}JW.W;^;W.W8[;W/W9[9W >X .Y<Z K[ 1Y@U 6~](~f'~[ ~V KX.Y IX.X" - " ?ZFY 6YAZ N~m 4Z 3Y !W?W 6p -WCk1ZB\\;Y :Y CZ %V <~e NX 6Z.Y 4Z M\\ J] EY9Z " - " L[ H^4[ 2[ 8\\<\\ BbKi ?` Ha @Z HV=X5X>W-Y6Y HZ2\\ Z !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZL] ,Z 2YBXGXBY(Y?Z" - "CZ*Z KZ\"x N[ LZ&x #f 3Y 9Z(Z HZ>Z\"ZCW>WCZ Hd &Z &[ #X HX FY At FY.Y JY $Y/Z JY,Y :Y 5Y.Y GY1Y 5Y NX" - " 0XM\\ MY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y (b Y .Y1Y CY;Y KYCWHXCY Bb 3Y=Y *[ 6e JX Ke KzF^ !U9Y7T'Z4[ CY7] @[E" - "XNX GZ.Y Ai 9Y1Y AY>Y 5YHZ ?Y1Y&[ IZ+ZAYAY HY9Y IY@X@Y KY/Y NZ.Y 5Y 5Y-Y IZ6Y 0[ 3Z 1Z LZ/Z;Z;Z*Z(Z&\\AZA[,[ L[" - "4~p BX B~o BX C~q CX NY?U 8y <W2W 3\\ )Y6Y JUEX NU KZCZ 7X &T=WGY7T -X J^ *Y1Y 7]EW 3Z " - " 8ZCZ 4X6UMV GX-X=^;W6UMW CY 4Y6Y DZ7Z CY6Y DZ7Z CY6Y DZ7Z#Z:Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#[)Y>ZCY*Z K" - "Z/Z KZ0Z L[1[ L[1Z KZ B_ C[>X7[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY@_ 5u <u <t :t <u <u!t,Y/Y #Y,Y MY,Y MY,Y MY,Y 7Y Y " - " NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Y LZ.Y KY.Z$~d$Y>XHZ KY0X HY0X GX0X GX0Y BY=Y BY.Y FY=Z 9WK~KW/WJ}JW.W:\\:W.W" - "9[:W/W9[9W >X .Z=Y JZ /X@U 6~^*~g&~Y N~V KX.Y IX.X ?ZFZ 7ZBY L~l 4Z 3Y \"X@X 3n /X" - "CZIZ2Z@\\<Y :Y BY %V <~e Y 6Z.Y 4Z N\\ G\\ FX8Z K[ I]2Z 2Z 8\\9[ BsNZ ?] B^ @Y GV=W4X>W.Z6" - "Z IZ1[ Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZK] -Z 2YBXHYBY(Y>ZDZ*Z KZ\"v L[ LZ&z !c 4Y 9Z(Z HZ>Z\"ZDX>XDY Ge 'Z '[ " - "\"X GX GY Dw FY.Y JY %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0XN\\ LY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y 'e $Y .Y1Y CZ=Z" - " KYDXGWDY @a 3Z>Y +[ 5d IX Ic L~d !U8X7T'Z4[ CY5\\ AZCa GY-Y @h 9Y1Y @X?Z 6ZGY ?Y1Y&[9X9Z+ZAYAZ IY9Y IY@X@Y " - "KY/Z Y-Y 5Y 5Y.Z IZ6Z 2[ 2Z 1Z M[/Z;Z<[*Z(Z%[AZB\\,[ LZ3~p BX B~o BX C~q CX NY?U 8y <W2W 2[ (Y7Y ITDW " - "NU M[CZ 6X &T=WFY8T -X EY1Y 1WEW 3Z 7ZC[ 6W6ULV HX+W JX7ULW CY 5Z6Z EY5Y DZ6Z EY5Y DZ6Z E" - "Z6Y$Z9Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#[)Y>ZCY*Z KZ/Z KZ0Z L[1[ L[1[ LZ A] B[?X6Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY?" - "_ 8w ?x ?w =w >w >w$~u/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY-Y$~d$Y?XFY KY0X HY0X GX0" - "X GX0Y BY>Z BY.Y EY>Y 8WK~KW/WJ}JW.W;]:W.W:[9W/W9[9W >X -Y>Z KZ .YAU 6~^*~g%~W L~T JX.Y IX.X ?YEZ 7Z" - "CZ L~k :y KY \"X@X 0m 1WCYEY3Y>\\=X 9Y BY %V <~e =l X 5Z.Y 4Z \\ E[ GY8Z JZ I]" - "2Z 2Z 8[7[ BqMZ ?^ C^ @Y GV=W4X>V-Y5Z IZ0[!Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZJ] .Z 2YAXIXAY(Y=YDZ*Z L[\"s" - " I[ LZ&[Cc Na 5Y 9Z(Z HZ?Z YDX>XEZ Hg (Z (\\ \"X GX GY Fy FY.Y KZ %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0e KY" - " FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y &h (Y .Y1Y BY=Y IXDXGWDY ?_ 1Y?Z ,[ 4b GX Ga L~c T6V6T'Z4[ CY4\\ CZ@_ GY-Y >f " - "9Y1Y @Y@Y 5YFZ @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z IY5Z 3[ 1Z 1Z M[/[<Z<[*Z(Z%\\BZC\\+[ LZ3~p BX B~o " - "BX C~q CX DX 4Z?U -Z (W2W 2Z 'Z7X ITDX U MZCZ 5X &U>WEY9T -X EY1Y 1WEW 3Z 6ZCZ 7X7" - "UKV HW*W KX6ULW CY 5Y5Z FZ5Z EY4Y FZ5Z EZ5Y EY5Z%Z9Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z KZ0Z L[0Z " - "LZ0[ LZ A] B[@X5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z4[ JY>` <y @y Ay ?y @y @y%~v/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY" - "-Y KY-Y MZ.Z MY-Y KY-Y$~d$Y?WEY KY0X HY0X GX0X GX0Y BZ?Y AY.Y EY>Y 8WK~KW/WJ}JW.W<_;W.W;[8W/W9[9W >X -Z?Z " - " LZ -YBU 5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW 'y JY \"W?X ,j 3WCYCY4Y=\\>X 9Y CZ" - " %V <~e =l X 5Z.Y 4Z !\\ C[ IY7Z JZ I]2Z 3[ 9[5[ BoLZ ?a Ia @Y HW>X3W>V.Z4Y IZ/Z!Z !Z#[\"Z MZ 0" - "Z Z'Z(Z :Z \"Z 4ZI] /Z 2YAXIXAY(Y=ZEZ*Z L[\"o DZ LZ&Z<^ M_ 5Y 9Z(Z GZ@Z ZEX>XEZ I[MZ (Z )\\ !X GX GY " - "Gz FY.Y KZ %Y-Y J~W :Y 5Y.Y GY1Y 5Y NX 0c IY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y %j +Y .Y1Y BY=Y IYEXGXEY >] 0Y?Y ,[ " - "3` EX E_ L\\Cx NT6V6T'Z4Z BY2Z CY>^ GY-Y ;c 9Y1Y @YAZ 6ZEY @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ" - "4Y 4\\ 1Z 1[ NZ.[<Z<Z)Z(Z$\\CZD]*Z LZ3~p BX B~o BX C~q CX DX 4Z?U -Z (W2W 2Z 'Z7X ITDX U MYBY 4X &U>" - "WDX:U -X EY1Y 1WEW 3Z 5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[ IZ !Z " - " Z !Z >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z L[0Z L[0Z LZ0[ LZ B_ BZAY5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY=` ?{ B{ Bz @z B{ " - "B{'~x/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y$~d$Y@WDY KY0X HY0X GX0X GX0Y AY@Z AY.Y " - "DY@Z 8WK~KW/WJ}JW.W=a<W.W<[7W/W9[9W >X ,Y?Y LZ +XBU 6~_+~i%~U I~P HX.Y IX.X @ZDZ 7YCY KX " - " (y JY \"W?W (h 5XCXAX5Z<\\@Y 9Y CZ $T ;~e =l X 5Z/Z 4Z \"\\ AZ IX6Z JZ I\\1[ 4Z 8Z3Z AmKZ" - " ?d d AZ HW>X3W>V.Z4Z JZ.Z\"[ \"Z#[\"Z MZ 0Z Z'Z(Z :Z \"Z 4ZH] 0Z 2YAYKX@Y(Y<ZFZ*[ M[\"Z /Z LZ&Z:\\ K" - "^ 6Y 9Z(Z GZAZ NZEW<WEZ IZL[ )Z *\\ X FX HY H{ FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0c IY FY3Y2Y+Y1Y" - " KZ-Y JY.Y Y-X ;Y $l .Y .Y1Y AY?Y HYEWFXEX =\\ .Y@Y -[ 2b GX Ga LY=s LT6W7T'Z4Z BY2Z DY=^ GY-Z =d 9Y1Y ?XAY" - " 5YDZ AY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ4Z 5[ 0Z 0Z NZ-Z<Z<Z)Z(Z#\\DZD\\)Z LZ3~p BX B~o BX B~p CX" - " DX 4Z?U -Z (W2W 2Z &[9X IUEX T s AXAY 4X &U>WCX;U -X EY1Y 1WEW 3Z Is 0YAX 8W6UJV IW)W" - " LX7UJW CY 6Z4Z GY3Y FZ4Z GY3Y FZ4Z GY3Z'Z8Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(Y<ZEY*[ M[/[ M[0Z LZ/Z LZ/Z LZ " - "Ca CZBY4Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY<` A| C| C{ A{ C| C|(~y/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y" - " MZ.Z MY-Y KY-Y$~d$YAWCY KY0X HY0X GX0X GX0Y AY@Y @Y.Y DY@Y 7WK~KW/XK}KX.W>c=W.W=[6W/X:[:X >X ,Y@Z M[ " - "+YCT 5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX )y HX #X@X (TNc 6WCX@X5Y:\\AX 8Y CZ :~e" - " =l !X 4Z/Z 4Z #\\ @[ KY6Z IZ I[0Z 4Z 9Z2[ @jJZ ?f %g AZ HW>X3W>V.Y2Y JZ.Z\"[ \"Z#Z!Z MZ 0Z Z'Z(Z" - " :Z \"Z 4ZG] 1Z 2Y@XKX@Y(Y<ZFZ*[ MZ!Z /Z LZ&Z8[ K] 6Y 9Z(Z FZBZ NZFX<XFY I[KZ )Z +\\ NX FX HY I| FY." - "Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0d JY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y #m 0Y .Y1Y AY?Y HYFXEWFY =\\ .YAY ,[ 2d I" - "X Ic LW8n JU7W7T'Y2Y BY1Z EY<\\ FY-Z @g 9Y1Y ?YBY 6ZDZ AY1Y&Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Y.Z JY3Z 6[" - " /Z 0Z [-[=Z=[)Z(Z#]EZE\\(Z LZ2~o BX B~n AX A~n BX DX 4Z?U -Z (X4X H~W <\\:W HUDX!T s AZCZ 5X %T>WBX<U" - " -X EY1Y 1WEW \"s 1ZCZ 9X7UIV JX)W LW7UIW CY 6Y2Y HZ3Z GY2Y HZ3Z GY2Y HZ3Z'Z8Z <[ IZ !Z Z !Z" - " >Z !Z !Z \"Z :Z#Z(Y<ZEY)Z M[/[ M[0[ MZ/Z LZ/Z M[ Dc DZCY3Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z6\\ IY:` D} D} D| B| D} D})~z" - "/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y$~d%ZBXCY KY0X HY0X GX0X GX0Y @YAY @Y.Y DYAZ " - " 7W8X8W.W HW-W?e>W.W>[5W.W:[:W =W +ZAY LZ *YDU 5~`,~i#~Q F} GX.Y IX.X AZBY 7ZEZ KX " - ")y HX 6~e 9TJ_ 7XCX?X6Y9\\BX 8Y CZ KX Nl !X 4Z/Z 4Z $\\ >Z LY5Z IZ I[0Z 5Z 8Z1Z >fHY =h " - " +i @Z HW>X3W?W/Z2Z KZ.[#[ \"Z#Z!Z MZ 0Z Z'Z(Z :Z \"Z 4ZF] 2Z 2Y@XLY@Y(Y;ZGZ*[ MZ!Z /Z M[&Z7[ K\\ 6Y 9Z(Z FZ" - "BZ MYFX<XGZ J[IZ *Z +[ MX FX HY Jb>Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0e KY FY3Y2Y+Y1Y KZ-Y JY.Y" - " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e JX Ke LU4k IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&" - "Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y JZ2Z 8[ .Z 0[!Z,[=Z=[)Z(Z\"]FZG]'Z M[1] 1X 1\\ @X @\\ L\\ AX DX 4" - "Z?U -Z (X4X H~W ;\\;W GTDX\"U s A[D[ 6X %T>WBX<T ,X EY1Y 1WEW \"s 2[D[ 9W7UHV KX(W MX7UI" - "W CY 7Z2Z IZ3Z HZ2Z IZ3Z HZ2Z IZ3Z(Z7Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(Y;ZFY)Z M[/[ MZ/[ MZ/Z M[/Z M[ Ee EZC" - "X3[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z8^ IY9` Fb=Y Eb=Y Eb=X Cb>Y Eb=Y Eb=Y*b=~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY" - "-Y LZ-Y MY-Z MY-Y LZ-Y CZCXBY KY0X HY0X GX0X GX0Y @YBZ @Y.Y CYBY 6W8X8W.W HW-W@g@X.W?[4W.W:[:W =W *YBZ " - " MZ (XDU 5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX +y GX 6~e 9TG] 8WBW>X6Y8\\DY 8Y CZ " - " KX Nl !X 4Z/Z 4Z %\\ =Z LX4Z IZ I[0Z 5Z 9Z0Z <bFY ;i 1i =Z HW>X3W?W/~S KZ-Z\"Z \"Z#Z!Z MZ 0[!Z" - "'Z(Z :Z \"Z 4ZE] 3Z 2Y?XMX?Y(Y;ZGZ)Z MZ!Z /[ N[&Z6[ K\\ 7Y 9Z(Z FZCZ LZGX<XGZ JZH[ +Z ,\\ MX FY IY K" - "]8Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0f LY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y Mk 3Y .Y1Y @YAY FYGWDXGY >^ .YCZ ." - "[ )_ KX L_ ES/e FU8Z9T'Z3X AY0Y FY:[ FY-Z Cj 9Y1Y >XCY 6ZBZ BY1Y&Z8X9[,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y J" - "Z2Z 9\\ .Z /Z!Z,\\>Z>[(Z(Z!]GZH^'[ N[0\\ 1X 2\\ ?X ?[ M\\ @X DX 4Z?U -Z 'W4W G~W :]>X GTDY#U s @[D[ 7" - "X %U?WAX>U ,X EY1Y 1WEW \"s 3ZC[ 9X7UHV KW(W MX7UHW CY 7~S J~S H~S I~S I~S I~S)} ;Z IZ !Z Z" - " !Z >Z !Z !Z \"Z :Z$[(Y;ZFY)Z MZ-Z MZ/[ N[/[ N[/Z MZ Eg F[EX2[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z9^ HY7_ G]8Y F^8Y F^8X D]8" - "Y E]8Y F^8Y+^8~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y BYDXAY KY0X HY0X GX0X GX0Y" - " @ZCY ?Y.Y CYBY 5W9X8W.W HW-WAiAW,WA[3W.W9Y9W >X *ZCZ 6~d IYET 4~`,~i!| By EX.Y IX.X AYAZ 7ZFY IX " - " Z 3X 6~e 9TF\\ 9WBX=W7Z7\\EX 7Y CZ KX Nl \"X 3Z/Z 4Z &\\ ;Z M~Z %Z I[0Z 6[ 9Z/" - "Y 8ZCZ 8i 6~d 5i ;Z HW>X3W?W0~T KZ-Z\"Z \"Z$[!Z MZ 0[!Z'Z(Z :Z \"Z 4ZD] 4Z 2Y?XMX?Y(Y:ZHZ)Z N[!Z /[ NZ%Z6[" - " J[ 7Y 9Z(Y DZDZ LZGW:WGZ K[GZ +Z -\\ LX EX IY L\\6Y FY.Y KZ %Y-Y K~W 9Y 5Y.Y GY1Y 5Y NX 0XM\\ MY " - "FY3Y2Y+Y1Y KZ.Z JY.Y Y-X ;Y Ji 4Y .Y1Y @YAY FYGWDXGX >` /YCY .[ $\\ LX M\\ AR+` CT9[:U'Z3X AY0Y FY9Z FY-Z " - "D` .Y1Y >YEZ 6YAZ BY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y LY.Z Y-Y 5Y 5Z/Y KZ1Z 9[ -Z /Z\"[+[>Z>[(Z(Z ^IZJ_&[ NZ.\\ 2X 3" - "\\ >X >[ \\ ?X DX 4Z?U -Z 'X6X G~W 9^@X GUDY$T Ns ?[CZ 8X %U?WAY?U ,X EY1Y 1WEW \"s 4" - "ZCZ 7W7UGV LX)X MW7UGW CY 8~T J~T I~S J~T I~T K~T*~ ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(Y:ZGY)[ NZ-Z N[.Z N[/[ N" - "[/[ NZ Fi G[FX1Z)Z(Z#Z(Z$Z(Z$Z)Z 9Z 2Z<a HY5^ I[5Y F[5Y G\\5X E\\6Y F\\6Y F[5Y+[5~V/Y #~V L~V L~W M~W 7Y Y NX" - " Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y BYDW@Y KY0X HY0X GX0X GX0Y ?YDZ ?Y.Y BYDY 4W9X9X.W HW-XC\\L[BW,WB[" - "3X.W HW >X )YCY 5~d IYFU 4~`,~i!{ @x EX.Y IX.X AY@Y 7ZGZ IX Z 3X 6~e 9TD[ ;XBX=X8" - "Z6\\GY 7Y CY JX Nl \"X 2Y/Z 4Z '\\ :Z M~Z %Z I[0Z 6Z 8Z/Z \"Z 5i 9~d 8i 8Z HW>X3W?W0~U LZ-Z\"[ " - "#Z$[!Z MZ /Z!Z'Z(Z :Z \"Z 4ZC] 5Z 2Y?XNY?Y(Y:ZHZ)[ [!Z .Z NZ%Z5[ K[ 7Y 9Z(Y DZDY KZHX:XHY K[EZ ,Z .\\ KX EX" - " IY LZ4Y FY.Y KZ %Z.Y KZ <Y 5Y.Y GY1Y 5Y NX 0XL\\ NY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y Ff 5Y .Y1Y @ZCZ FY" - "HXCWHY ?b /YDY /[ ![ MX M[ @Q%W ?T9\\;U'Z3X AY0Z GX8Z FY-Z E\\ )Y1Y =XEY 6Z@Y BY1Y&Z9Y9[,ZAYAZ IY9Y IY@X@Y " - "LY.Z Y-Y 5Y 4Y/Y KZ0Z ;[ ,Z /[#Z*\\?Z?\\(Z(Z N`LZL`$Z NZ-\\ 3X 4\\ JPCXCP J[\"\\ >X DX 4Z?U -Z 'X6X G~W " - "8^BX FUDY%U Ns =ZCZ 9X $U@W@X?T +X EY1Y 1WEW \"s 5ZCZ 7W7UFV LW(W MX8UFW CY 8~U K~T J~U K~" - "T J~U K~T*~ ;[ JZ !Z Z !Z >Z !Z !Z \"Z :Z$Z'Y9YGY)[ [-[ [.Z N[.Z NZ.[ NZ G\\L[ GZGX0Z)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2~ " - "GY4] J[4Y G[4Y G[4X EZ4Y FZ4Y G[4Y,[4X 1Y #Y Y Y Y 9Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y BYEW?Y" - " KY0X HY0X GX0X GX0Y ?YDY >Y.Y BYDY 4W9X9W-X JX,WD\\J[CW,WC[2W-X JX >X )YDZ 5~d HXFU 4~_+~i z @w DX.Y" - " IX.X BZ@Y 6YGZ IY Y @~e 9TCZ ;WAX=X8Y4\\HX 6Y CY JX Mj !X 2Y/Y 3Z (\\ 9Z" - " M~Z %Z I[0Z 6Z 8Z/Z \"Z 2i <~d ;i 5Z HW>X3W@W/~U LZ-[#[ #Z$Z Z MZ /Z!Z'Z(Z :Z \"Z 4ZB] 6Z 2Y>a>Y(Y9ZIZ)[ " - "Z Z .Z [%Z4Z JZ 7Y 9Z)Z DZEZ JYHX:XIZ KZD[ -Z /\\ JX EX IY MZ3Y FY.Y JY %Z/Z JY <Y 5Y.Y GY1Y 5Y NX" - " 0XK\\ Y FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y Bc 6Y .Y1Y ?YCY DYIXCXIY @c /YEY /[ NZ MX N[ *U;^=U&Z4Y AY/Y HY7X" - " EY-Y E[ 'Y1Y =YFY 6Z@Z CY1Y&Z9Y9Z+ZAYAZ IY9Y IY@X@Y LZ/Z Y-Y 5Y 4Y0Z KZ0Z <[ +Z .Z$[)\\@Z@\\'Z(Z M~Q#Z [,\\ 4" - "X 5\\ JRDXDR J[$\\ KQCXDQ #Y 4Z?U -Z &X8X F~W 7_EY EUDY&U Ns <ZCZ :X $U@W?XAU +X EY1Y 1WEW " - " \"s 6ZCZ 7X8UEV MX)X MW7UFW DZ 8~U L~V K~U L~V K~U K~U+~ :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y9ZHY(Z [-[ Z" - "-[ Z-Z [-Z [ H\\J[ HZHY1[)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2} FY2\\ KZ3Y GZ3Y GY3Y FZ3Y GZ3Y GZ3Y,Z3X 1Y #Y Y Y Y 9Y Y " - "NX Y 6Y-Z JX0X JY-Y KY.Z MZ.Z MY-Y KY-Y BYFX?Y KY0X HY0X GX0X GX0Y >YEY >Y.Y BYEZ 4X:X9W,W JW+WE\\H[EX,X" - "E[1W,W JW =X )ZEY 4~d HYHU 2~^+~i Nx >u CX.Y IX.X BY?Z 7ZHY GX Z A~e 9TCZ <XAW<" - "X8Z4\\JY 6Z DY JX 4X 1Z0Y 3Z )\\ 8Z M~Z %Z I[0Z 7Z 7Z/Z \"Y /i >~d >i 2Z GV>X3W@W0~V LZ-[\"Z " - "#Z%[ Z MZ /[\"Z'Z(Z :Z \"Z 4ZA] 7Z 2Y>a>Y(Y9ZIZ(Z Z Z .[![%Z4[ KZ 7Y 9Z)Z CZFZ JZIX:XIZ L[CZ -Z /[ IX DX J" - "Y MY2Y FY.Y JY %Z/Z JY <Y 5Y.Y GY1Y 5Y NX 0XJ\\ !Y FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y ?a 7Y .Y1Y ?YCY DYIWBX" - "IY @d /YFY 0[ LY MX NZ )U<VNW=U&Z4Y AY/Y HY8Y EZ.Y F[ &Y1Y =YGZ 7Z>Y CY1Y&Z9Y9Z+ZAYAY HY9Y IY@X@Y LZ/Y N" - "Y-Y 5Y 4Y0Z LZ.Y =[ *Z .[%Z(]AZA]'Z(Z L~\"[![+\\ 5X 6\\ JTEXET J[&\\ KSDXES $Y 3Y?U -Z &Y:Y F~W 5_GX DU" - "CZ9QAU DZCZ ;X $VAW?YBU +X EY1Y 1WEW DZCZ 6W7UEV NX)X MX8UEW DY 8~V L~V L~W M~V K~V M~V" - ",~P :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY(Z Z+Z Z-[![-[![-[![ I\\H[ I[JY0[(Y(Z#Z(Z$Z)Z#Z)Z 9Z 2| EY1\\ LY2Y " - "HZ2Y HZ3Y FY2Y GY2Y GY2Y-Z2X 1Y #Y Y Y Y 9Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY.Z BYGX?Z KY1Y HY0X" - " GX0X GX0Y >YFZ >Y.Y AYFY 2W:X:X,W JW+XG\\F[FW+XF[1X,W JW =X (YEY 4~d GXHU 2kNRMk*tNq Mv <s BX.Y IY/X" - " BY>Y 7ZIZ GY !Z A~e 9TBY <W@W;W8Z3\\KX 5Z DY JX 4X 1Z1Z 3Z *\\ 7Z M~Z %" - "Z HZ0Z 7Z 7Y.Z #Z ,i A~d Aj 0Z GV=W4X@W0~W MZ-[\"[ $Z%[ Z MZ /[\"Z'Z(Z :Z \"Z 4Z@] 8Z 2Y>`=Y(Y8ZJZ([\"[ Z " - ".[!Z$Z3Z KZ 7Y 9Z)Z CZGZ IZIW8WIZ M[AZ .Z 0\\ IX DX JY MY2Y FY.Y JY $Y/Z JY <Y 5Z/Y GY1Y 5Y NX 0XI" - "\\ \"Y FY3Y2Y+Y1Y JY.Z JY.Y NY/Y ;Y ;] 7Y .Y1Y >YEY CYIWBXIX @f 0YGZ 0[ LZ NX NY 'U>WMW?V&Z4Y AY/Y HY8Y" - " EZ.Y FZ %Y1Y <XGY 6Z>Y CY1Y&[:Z:Z+ZAYAY HY9Y IY@X@Y LZ/Y NZ.Y 5Y 4Y0Y KZ.Z ?\\ *Z -['['\\AZB]&Z(Z K|![!Z)\\ 6" - "X 7\\ JVFXFV J[(\\ KUEXFU %Y 3Y?U -Z %Y<Y /Z M`KY BUC[=SAU CZCZ <X #UAW>XCU *X EY1Y 1WEW" - " F[CZ 6X8UDV NW)X MX8UDW DY 8~W N~W L~W M~V L~W M~W-~P :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY([\"[+[" - "\"[,Z![-[!Z,[!Z I\\F[ J[KY/Z'Z)Z#Z)Z#Z)Z#Z)Z 9Z 2{ DY0[ MY1Y HY1Y HY2Y FY2Y HZ2Y HY1Y-Y2Y 1Z $Y Y Y Z :Y Y" - " NX Y 6Z.Y IX0X JZ.Y KY.Z MZ.Y LZ.Y KY.Z BYHX>Z KY1Y HY1Y GX0X GX0Y =YGY =Y.Y AYFY 2X;X:W+X LX*WH\\D[HX" - "*WG[0W+X LX =X (YFZ 4~d GYIU 2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX !Z A~e 9T" - "BZ >W?W;W8Z2\\MY 4Y DY JX 4X 1Z1Z 3Z +\\ 6Z M~Z %Z HZ0Z 8[ 7Y.Z #Z )i D~d Ci -Z GV=W4XAW/~W M" - "Z-[\"[ $Z&[ NZ MZ .Z\"Z'Z(Z :Z \"Z 4Z?] 9Z 2Y=_=Y(Y8ZJZ([\"[ Z -Z\"[$Z3[ L[ 8Y 9Z)Z BZHZ IZJX8XJY LZ@[ /Z 1\\" - " HX DX JY NY1Y FZ0Z JY $Y/Z JY <Y 5Z0Z GY1Y 5Y NX 0XH\\ #Y FY3Y2Y+Y1Y JY.Y IY/Z NY/Y ;Y 9\\ 8Y .Y1" - "Y >YEY BXJXAWJY A[N[ 1YGY 0[ JY NX NY 'V@WLX@U$Y5[ BY/Y HX7X DZ.Y FY $Y1Y <YIZ 6Y=Z DY1Y&[:Z:Z*YAYAY HY9" - "Y IY@X@Y LZ/Y NZ/Z 5Y 3Y1Y KY-Z ?[ )Z -[([%]CZC]%Z(Z Jy M[#[(\\ 7X 8\\ JXGXGX J[*\\ KWFXGW &Y 3Y?U -Z %Z>Z " - "/Z K_MZ BUC]BVBU A[D[ >X #VBW=XDU *X EY1Y 1WEW G[D[ 5W8UCV X*X LW8UCW EZ 8~W N~X M" - "~W N~X M~W N~X.~Q :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&Y7ZJY([\"[+[\"[,[\"Z+[#[+Z\"[ J\\D[ JZKX/['Z*[#[*Z#Z)Z#Z)Z" - " 9Z 2z CY/Z MY1Y HY2Z HY2Y GY1Y HY1Y HY1Y-Y2Z 2Z $Z !Z !Z !Z :Y Y NX Y 6Z.Y IX0X JZ/Z KY.Z LY.Y LZ/Z KY.Z " - " BYHW=Z KY1Y GX1Y GX1Y GX0Y =YHZ =Y/Z @YHY 1X;X;X*W LW)XJ\\B[IX*XI[0X*W LW <X (ZGY 3~d GYJU 1iKQKi*pN" - "RMo Jr 9q AX.Y HX0Y CZ>Z 7ZJY EY !Z 1X@X &TAY ?X?W;W8Z1\\NX 3Y DY JX 5Y 0" - "Y1Z 3Z ,\\ 5Z M~Z %Z HZ0Z 8Z 6Y.Z #Z &i G~d Fi )X FV=X5XAW0~Y NZ-[!Z $Z&[ NZ MZ .[#Z'Z(Z :Z \"Z 4Z>] :Z 2" - "Y=_=Y(Y7ZKZ'Z#[ NZ -[#[$Z2[ M[ 8Y 9Z)Z BZHZ HYJX8XKZ M[?Z /Z 2\\ GX CX KY NY1Y FZ0Z JZ %Y/Z JZ =Y 4" - "Y0Z GY1Y 5Y NX 0XG\\ $Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 8[ 8Y .Y1Y >ZGZ BYKXAXKY B[LZ 0YHY 1[ IY NX Z &VB" - "XKXBV$Y5[ BY/Y HX8Y CY/Z GY #Y1Y <YIY 6Z<Y DY1Y%Z:Z:Z*YAYAY HY9Y IY@X@Y LZ/Y NZ/Z 5Y 3Y2Z LZ,Z A[ (Z ,[)[%^DZD^" - "%Z(Z Iw L[#['\\ 8X 9\\ JZHXHZ J[,\\ KYGXHY 'Y 3Z@U -Z $[B[ .Z NW $j @UCpBU @[D[ ?X \"UBW=XEU )X " - " EY1Y 1WEW H[D[ 5W8UBV W*X LX8UCW F[ 9~Y ~X N~Y ~X N~Y ~X.~Q 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z&[&" - "Y7ZJY'[#Z)Z#[+[#[+[#[+[#[ K\\B[ K[MX.['Z*Z!Z*Z#Z)Z#Z)Z 9Z 2x AY.Z NY2Z HY2Z IY1Y GY1Y HY1Y HY2Z-X1Z 2Z $Z !Z !Z" - " !Z :Y Y NX Y 5Y/Z IX0X JZ/Z KZ/Y KY.Y LZ/Z KZ/Y AYIW<Y IX1Y GX1Y GX1Y GY2Z =YHY <Z0Y ?YHY 0X<X;X*X N" - "X)XJ[@[KX(XK[/X*X NX <X 'YHZ 3~d FXJU 0hKQKh(nMRMo Jq 7o @X.Y HX0X BY=Z 7ZKZ DY \"Z " - " 1W?X &TAY ?W>W;W8Z0e 3Y EZ JX 5X /Z2Y 2Z -\\ 4Z M~Z %Z HZ0Z 8Z 6Z/Z $Z #j J~d Ii CW>X6Y" - "BX0~Y NZ-[![ %Z'\\ NZ MZ -Z#Z'Z(Z :Z \"Z 4Z=] ;Z 2Y<]<Y(Y7ZKZ'[$[ NZ -[$[#Z1Z M[ 8Y 8Z*Z BZIZ GZKX8XKZ N[>[ 0" - "Z 3\\ FX CX KY NY2Z FZ0Y IZ %Y/Z JZ =Y 4Y0Z GY1Y 5Y NX 0XF\\ %Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 7Z 8Y" - " .Y2Z =YGY AYKW@XKY BZJZ 1YIY 1[ HY NX Y %WEYIYFW#Y5[ BY/Y HX8Y CY/Z GY #Y1Y ;XIY 6Y;Z EY1Y%Z:Z:Z*ZBYBZ " - "HY9Y IY@X@Y LZ/Y MY/Z 4Y 4Y2Y KZ,Z B[ 'Z +[+[#_FZF_$Z(Z Gt JZ$[%\\ 9X :\\ J\\IXI[ I\\/\\ K[HXI[ (Y 3Z@U -Z " - "%^F^ /Z X \"f >VBnCU >[D[ @X \"VCW<XGV )X EY1Y 1WEW I[D[ 5X8UBV!W)X LW8UBW FZ 8~Y!~Z" - " ~Y!~Z ~Y ~Y0~R 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%Y6ZKY'[$[)[$[*[$[*[%[*[$[ K\\@[ Le.[&Z*Z!Z*Z\"Z*Z#Z*[ 9Z 2v " - "?Y.Z NY2Z HX1Z IY1Y GY1Y HY2Z HX1Z.Y1Z 1Y #Y Y Y Y :Y Y NX Y 5Y/Z IX0X IY/Z KZ/Y KY/Z KY/Z KZ/Y 7\\ 7ZKW" - ";Y IX1Y GX1Y GY2Y GY2Z <YIY <Z0Y ?YIZ 0X<X<X)X Y(XJY>YJX(XJY/X)X Y <X 'ZIZ 3~d FYKT /gJQJg(nMRLm Hp 6" - "m ?X.Y HY1X CZ<Y 6YKZ DY \"Z 1W?W %TAY @X>W;W7Y/c 2Y EY IX 5X /Z3Z 2Z .\\" - " 3Z M~Z &Z FY1Z 8[ 6Z/Z $Z i L~d Li @W>Y7YBW0Z*Y NZ-[![ %Z'[ MZ MZ -[$Z'Z(Z :Z \"Z 4Z<] <Z 2Y<]<Y(Y6ZL" - "Z'[%[ MZ ,[%[#Z1[ N[ 8Y 8Z+[ AZJZ GZKW6WKZ NZ<[ 1Z 3[ EX CX KY NY2Z FZ0Y IZ %Z1[ IY =Y 4Z1Z GY1Y 5Y" - " NX 0XE\\ &Y FY3Y2Y+Y1Y JZ0Z IZ0Y MZ1Z ;Y 6Y 8Y .Y2Z =YGY AYKW?WKX B[J[ 1YJY 2[ GY NX Y $ZL[H[JY#Y6\\ " - "BY0Y GX8X BZ0Z GY #Y1Y ;YKZ 7Z:Y EY2Z%Z:Z:Z*ZBYBZ HY9Y IY@X@Y L[1Z MY/Y 3Y 4Z3Y LZ+Z C\\ 'Z +[,[!_GZG_#Z(Z Fq H" - "[%[$\\ :X ;\\ H\\JXJ\\ H\\1\\ J\\IXJ\\ (Y 3Z@U -Z &x 0Z X c <UAmDV =[CZ AX !VDW<YHU (X E" - "Y1Y 1WEW JZCZ 3W8UAV\"X*X LX9UAW G[ 9Z*Y!Z+Z Y*Y!Z+Z Y*Z\"Z+Z0Z3Z 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%" - "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s <Y-Y NX1Z IY1Z IY2Z GY2Z HY2Z HX1Z.Y1Z 1Z $Y Y " - "Z !Z ;Y Y NX Y 5Y/Z IX0X IY/Y JZ0Z KZ0Z KY/Y IY0Z 7\\ 6YLX<Z IX1Y GY2Y GY2Y GY2Z <YJZ <Z0Y >YJY .X=X=Y(" - "X!X'YJW<WJX&XJW/Y(X!X ;X &YIY #[ LYLU .fJQJf&lLRLm Gn 4k >X.Y HY2Y CZ<Z 7YKY BY #[ " - " 3X@X %TAY @W=W;W7Z0b 1Y EY IX 5X /Z3Z 2Z /\\ 2Z )Z JZ FZ2Z 8Z 5Z/Z %Z Ki :j >W=X8ZC" - "W/Z*Z Z-Z N[ &Z(\\ MZ MZ -\\%Z'Z(Z :Z \"Z 4Z;] =Z 2Y<]<Y(Y6ZLZ&[&[ MZ ,\\'[\"Z0Z NZ 7Y 8Z+Z @ZJY FZLX6XLY N[;" - "Z 1Z 4\\ EX BX LY NY2Z F[2Z HZ %Y1[ IZ >Y 4Z2[ GY1Y 5Y NX 0XD\\ 'Y FY3Y2Y+Y1Y IY0Z IZ1Z MZ1Z ;Y 6Y" - " 8Y .Y2Z =ZIZ @XLX?WLY C[H[ 2YKZ 3[ EX NX Y $hFh\"Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y ;YKY 6Y9Y EY2Z%Z;[:Z*ZBYB" - "Y GY9Y IY@XAZ L[1Y LZ1Z 3Y 3Y3Y LZ*Z D[ &Z *[-[ aJZJa\"Z(Z Cl F\\'[\"\\ ;X <\\ F\\KXK\\ F\\3\\ H\\JXK\\ 'Y " - "2ZAU -Z 'z 1Z X Na ;V@jDV :ZCZ BX UDW;XIU 'X EY2Z 1WEW KZCZ 3X9U@V\"W*X LX9VAW H[ " - "8Z*Z\"Y)Y!Z*Z\"Z*Y!Z*Z\"Z*Z1Z3Z 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%Y5ZLY&[&['[&[([&[)\\'\\)[&[ L\\<[ Mc.[$Z,[!" - "[,[\"Z+Z!Z+Z 8Z 2n 7Y-Y NX1Z IY2[ IY2Z GY2Z HY2Z IY2[.Y2\\ 2Z $Z !Z !Z !Z ;Y Y NX Y 5Z0Y HX0X IZ1Z IY0Z KZ0" - "Y JZ1Z IZ1Z 7\\ 6YMX;Z IY3Z GY2Y GY2Y GY2Z ;YKY ;Z1Z >YJY .Y>X=X'Y#Y&XIU:UJY&YJU.X'Y#Y ;X &YJZ #Z JXLU" - " -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ #Z 3X@X %TAX @W<W;W7Z/a 0Y FY IX " - " 6X -Z4Z 2Z 0\\ 2[ )Z JZ FZ2Z 8Z 5Z/Z %Z Hi @j :V=Y9ZDX/Z*Z Z-Z N\\ 'Z)\\ LZ MZ ,[%Z'Z(Z :Z \"Z" - " 4Z:] >Z 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[ Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\ DX BX LY NY3[ F[2Z HZ %Y1" - "[ IZ >Y 3Y2[ GY1Y 5Y NX 0XC\\ (Y FY3Y2Y+Y1Y IZ2Z H[2Z LY1Z ;Y 6Z 9Y .Y2Z <YIY ?YMX?XMY CZFZ 2YKY 3[ DY X " - "Y #gEf!Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y :XLZ 7Z9Z FY2Z%Z;\\<[)ZCYCZ GY9Y IZAXAZ L[1Y LZ1Z 3Y 3Y4Y KZ*Z E[ %Z )[" - "/[ MdNZNd!Z(Z Ag B['[!\\ <X =\\ D\\LXL\\ D[4\\ F\\KXL\\ &Z 3ZAU -Z (| 2Z X L^ 9V?fBU 8ZCZ CX V JV " - " CY2Z 1WEW LZCZ 2W9V@V#X+X KW8U@W I[ 7Z*Z#Z)Z\"Z)Y#Z)Z\"Z)Y#Z)Z2Z2Z 7[ NZ !Z Z !Z >Z !Z" - " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2" - "[.Y2\\ 2Z $Z !Z !Z Y ;Y Y NX Y 5Z1Z HX0X IZ1Z IZ1Z JZ2Z JZ1Z IZ1Z 7\\ 6c:Z IY3Z GY3Z GY3Z GY3[ ;YKY ;[2Z =" - "YLY ,Y?X>Y&Y%Y%YIS8SJY$YJS.Y&Y%Y :X &ZKY #Z IYNU ,cISIb#jKRJi Cj 1i =X.Y GY4Y BY:Y 7ZMZ AZ " - " $[,P )W?X %TBY AX<W;W7[/_ /Y FY IX 6X -Z5Z 1Z 1\\ 1Z (Z K[ EY2Z 9Z 4Z0[ &[ F" - "j Ei 7W=Y;[EX/Z(Z!Z.[ M[!P'Z*] LZ MZ ,\\&Z'Z(Z :Z \"Z 4Z9] ?Z 2Y;[;Y(Y4YMZ%[)\\ LZ +\\)[!Z/Z Z 7Y 7Z-[ ?Z" - "LZ EZMX6XMZ Z8[ 3Z 6\\ CX BX LY NY3[ F[2Y GZ %Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0XB\\ )Y FY3Y2Y+Y1Y IZ2Z " - "H[2Y KZ3[ ;Y 6Z 9Y .Z4[ <YIY ?YMW>XMY DZDZ 2YLY 3[ DY X Y \"eCd NY8^ CY0Y GX:Y @Z2Z FX \"Y1Y :YMY 6Y7Y " - "FY2Z%[<\\<Z(ZCYCZ GY9Y HYAXAY K\\3Z KZ2Z 3Y 3Z5Y LZ)Z F[ $Z ([1[ K~U Z(Z ;[ <\\)[ N[ <X <Z B\\MXM\\ BZ3Z D\\L" - "XM\\ %Z 3ZAU -Z )~ 3Z X J] 9V>a@V 7YBY CX NV LV BZ3Z 1WEW LYBY 2W8U?V#W+X KX9U" - "?W J[ 7Z(Y#Z)Z#Z(Z$Z)Z\"Y(Z$Z(Y2Z2Z 7\\\"P NZ !Z Z !Z >Z !Z !Z \"Z :Z*\\#Y4ZMY%\\)[%[)\\&[)\\'\\)\\'\\)[ M\\8" - "[ N`-[#Z,Z NZ,Z Z-[![-[ 8Z 2Z #Y-Y NX2[ IY2[ IY3[ GY3[ HY3[ HX2[.Y3^ 2Z $Z !Z !Z !Z <Y Y NX Y 4Z2Z HX0X HZ2" - "Z IZ2Z IZ2Z IZ2Z IZ2Z 6\\ 5a:Z HY3Z GY3Z GY3Z GY3[ ;YLY :[2Y <YLY ,Y?X?Y$Y'Y#YIQ6QIY$YIQ.Y$Y'Y 9X %YLZ " - "$Z HYNU +aHSH`!hJRIg Bi /g <X.Y GY4Y CZ:Y 6YMY @[ $Z-Q )W?W $TBY AW;W<X6Z.] .Y GY" - " HX 6X -Z5Z 1Z 2\\ 0Z (Z L[ DZ4Z 8Z 4[1Z %Z Bj Ki 4W=Z=\\GY.Z(Z!Z.[ M\\#Q'Z+] KZ MZ +\\'Z" - "'Z(Z :Z \"Z 4Z8] @Z 2Y:Y:Y(Y4ZNZ%\\*[ KZ *\\+\\!Z/[ \"[ 7Y 7Z-Z >ZMZ DZMW4WMZ![7Z 3Z 7\\ BX AX MY NY3" - "[ F\\4Z FZ &Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0X@[ *Y FY3Y2Y+Y1Y HZ3Z H\\4Z KZ3[ ;Y 5Y 9Y -Y4[ ;YKY >YNX=WNY D[D[ " - "3YMY 3[ CY X Y !cAb MZ9^ CZ2Z GX:Y @Z3Z EX \"Y1Y :YMY 7Z7Y FZ4[$Z<\\<Z(ZCYCY FY9Y HYAXBZ K\\3Z KZ3Z 2Y 2" - "Y6Z LZ(Z H\\ $Z (\\3[ I~R MZ(Z :Z ;\\+\\ MY ;X ;X @\\NXN\\ @X1X B\\MXN\\ $Z 2ZBU -Z *~Q 4Z X I] :W9U;V " - " 5XAX CX MV NV AZ3Z 1WEW LXAX 2X8s+W,Y JW8t#\\ 7Z(Z%Z'Y#Z(Z$Y'Z$Z(Z$Z(Z4Z1Z 6[#Q NZ !" - "Z Z !Z >Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3" - "\\ IY3[ GY3[ HY4\\ HX3\\.Y3^ 2Z $Z !Z !Z !Z <Y Y NX Y 4Z3Z GX0X HZ3Z GZ3Z IZ3[ IZ3Z GZ3Z 6\\ 5`9Z HY4[ GY4[" - " GZ5[ GZ5\\ :YMY :\\4Z ;XMZ +Y@X@Y#Z)Z\"Y(Y\"Y(Y#Z)Z 9X %ZMZ %Z F_ )^GSG^ NfIRHe @g -e ;X.Y GZ6Z CY9" - "Z 7ZNY ?[ %[/R *X@X $TBY BX;X=X6[.] /Y GY HX 7X +Z7Z 0Z 3\\ 0[ (Z L[ DZ4" - "Z 9[ 3Z2[ &Z >i i 2W<Z?]HZ.Y'Z!Z/\\ L\\&S'Z,] JZ MZ *\\(Z'Z(Z :Z \"Z 4Z7] AZ 2Y JY(Y3e$\\,\\ KZ )\\-" - "\\ Z.Z \"[ 7Y 7[/[ =ZNZ DZNX4XNY![6[ 4Z 7[ AX AX MY NY4\\ F\\4Z F[ &Z5] H[ @Y 2Z6] GY1Y 5Y NX 0X?[ +" - "Y FY3Y2Y+Y1Y HZ4Z G\\4Z JZ5\\ ;Y 6Y 8Y -Y5\\ ;YKY =XNX=WNY E[B[ 3YNY 4[ BY X Y N_=_ LZ:_ CZ2Y FX;Y >Z4" - "Z EY #Y1Y 9XNZ 7Y6Z GZ4[$Z=]=['ZDYDZ FY9Y HZBXBZ K]5Z J[5[ 2Y 2Z7Y L[(Z H[ #Z '\\5[ F~ LZ(Z :Z :\\-\\ KW :X :" - "V >r >V/V @s #Z 2[CU -Z +[MeL[ 5Z X G\\ :W!V 3W@W 7V!W AZ4[ 1WEW LW@W 1W7s,X-" - "Y JX8t$\\ 7Z'Z%Z'Z$Z'Y%Z'Z$Z'Y%Z'Z4Z1Z 6\\&S NZ !Z Z !Z >Z !Z !Z \"Z :Z,]\"Y3ZNY$\\,\\#\\,\\$\\,\\$\\-\\$\\," - "\\ N\\4[ ]-\\![/Z LZ/[ N[/[ N[/[ 7Z 2Z #Y-Y NY4\\ HY5] IY4\\ GY4\\ HY4\\ HY4\\.Z5` 2Z $Z !Z !Z !Z =Y Y NX Y " - "3Z4Z GX0X H[5[ GZ4Z GZ4Z H[5[ GZ4[ 6\\ 5_9[ HZ5[ GZ5[ FY5[ FY5\\ :YNZ :\\4Z ;YNY )YAXAZ\"Z+Z!Z*Y Y*Z\"Z+Z 8" - "X $YMY %[ F^ '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[ %[1S -Y 'X@X ;Q:TCZ CX:X=X" - "5[.] /Y HY HX NZ GZ 'X +[8Z 0Z 4\\ 0[ 'Z M\\ CZ6[ 9Z 2[3[ '[ 0Y Y ?f f BX DW=\\C_J[.Z&Z\"Z0\\ " - "J\\(T'Z._ JZ MZ *])Z'Z(Z :Z \"Z 4Z6] BZ 2Y JY(Y3e#\\.\\ JZ )]/\\ NZ.[ NQ'[ 6Y 6[0[ =ZNZ CYNX4XNY!Z4[ 5Z 8[ @X" - " AX MY NY5] F]6Z DZ &Z5] G[ AY 2[8^ GY1Y 5Y NX 0X>[ ,Y FY3Y2Y+Y1Y H[6[ G]6Z IZ5\\ ;Y 6Y 8Y -Z6\\ ;Z" - "MZ =b=b EZ@Z 3d 5[ AY X Y L[:\\ IZ;` D[4Z FX<Z >Z5[ EY #Y1Y 9c 7Z5Y GZ5\\$[>^>['[EYE[ FY9Y HZBXCZ J]5Z " - "IZ5Z 1Y 1Y8Z LZ&Z J[ \"Z &\\8] E| KZ(Z :Z :]/] JU 9X 9T <p <T-T >q \"Z 1ZCU -Z ,[JaI[ 6Z X F\\ :W#V 1" - "V?V 7W#W @[5[ 1WEW LV?V 1X7s,W-Y JX7t%\\ 6Z&Z&Z'Z%Z&Z&Z'Z%Z&Z&Z&Y4Y0Z 5\\(T NZ !Z Z " - "!Z >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY" - "5] HY5] HY5]-Y5a 3[ %[ \"[ \"[ \"[ >Y Y NX Y 3Z5[ GX0X GZ5Z F[6[ G[6[ GZ5Z F[5Z 5\\ 4^9Z FY6\\ FY6\\ FY6\\ " - "FY6] 9c 9]6Z :d )[CXBZ Z-Z NZ-[ [-Z Z-Z 7X $YNZ %Z D] $VCSDW G`FSG` ;d +c :X.Y F[9Z CZ8Y 6d =\\ " - " '\\3T -Z (W?X ;S<TDZ BW8W=W4\\1` 0Y HY HX NZ GZ 'X *Z9Z /Z 5\\ 0\\ 'Z N\\ B[8[ 8Z" - " 2\\5[ '[ /Z \"[ >d c @Z EW<_Ks-Z&Z\"Z1] J^,V'Z/_ IZ MZ )]*Z'Z(Z :Z \"Z 4Z5] CZ 2Y JY(Y2d#]0\\ IZ (]1] NZ-" - "Z NS*\\ 6Y 6[1[ <e Bc4c\"[3Z 5Z 9\\ @X AX MY NZ6] F^8[ D[ &Z7^ G[ AY 1[:_ GY1Y 5Y NX 0X=[ -Y FY3Y2Y" - "+Y1Y G[7Z F]7[ HZ7] ;Y 6Y 7Y .Z7] :YMY <a<a EZ>Z 4c 5[ @Y X Y HS3V FZ<a D\\5Z FX<Y =[7[ DZ $Y1Y 9c 7Y4" - "Z H[6\\#Z?WNV>Z%ZEYF[ EY9Y GZCXD[ J^7Z H[7[ 1Y 1Z:Z KZ&Z K[ !Z %];] Bx IZ(Z :Z 9]1] HS 8X 8R :n :R+R <o !Z " - "1[DU -Z -[F\\F[ 7Z X E\\ :W&W /U>U 6W%W ?[6\\ 1WEW LU>U 0W6s-X.X HW6t&\\ 5Z&Z'Z" - "%Z&Z&Z'Z%Z&Z&Z&Z&Z6Z0Z 4],V NZ !Z Z !Z >Z !Z !Z \"Z :Z0`!Y2d\"\\0]!]0\\!]0\\!]1]!]1] \\0[ ]1] N[2\\ L\\2[ L\\" - "2[ L[1[ 6Z 2Z #Y.Y MZ7^ HY6^ HY6] GZ6] HZ7^ HZ7^-Y6c 3[ %[ \"[ \"[ \"[ ?Y Y NX Y 3[7[ FX0X G[7[ E[7[ FZ7[ F" - "[7[ E[7[ 5\\ 4]9[ FZ8] FZ8] FZ8] FZ7] 9c 9]7[ 9b '[DXD[ N[/Z LZ/[ M[0[ N[/Z 6X $d %Z C\\ ?S 2\\ETD" - "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\ '\\5U -Z (W?W :U>TE[ CX8X?X3\\3b 1Y IY GX NZ GZ (" - "X )[;[ /Z 5[ %Q-\\ &Z BQ/] AZ9\\ 9Z 0[6\\ (\\ /Z \"[ ;a ` =Z EX<nNd,Z$Y\"Z2] H^.W'Z2a HZ MZ (^,Z'Z(Z :Z \"" - "Z 4Z4] DZ 2Y JY(Y2d\"]3^ IZ ']3] MZ-[ U-] 6Y 5\\4\\ ;d Bb2b#[2[ 6Z :\\ ?X @X NY MZ8^ F^8Z B[ '[9_ F[," - "P 7Y 1\\<` GY1Y 5Y NX 0X<[ .Y FY3Y2Y+Y1Y G[8[ F^9[ G[9^ ;Y *Q/Z 7Y -Z9^ :YMY <a;` F[>[ 4b 6[ ?Y X Y " - "FZ=b E]7Z EX=Z <[9\\ D[ %Y1Y 8a 6Y3Y H\\8]#[@WNW@[%[FYG\\ EY9Y G[DXD[ J_9[ G[9[ /Y 1Z;Z LZ%Z L\\ !Z $]=\\ >t GZ" - "(Z :Z 8]3] FQ 7X 7P 8l 8P)P :m Z 0[EU -Z .[?P?[ 8Z X D[ 9W(W -T<S 5X)X >\\8] 1WEW " - " LS<T 0W5s-W.X HX6t'\\ 5Z$Y'Z%Z'[%Z(Z%Z&Z%Z(Z%Z6Z0Z 4^.W NZ !Z Z !Z >Z !Z !Z \"Z :Z2a Y2d\"^3] N]3^ ]3" - "] N]3] N]3] \\.[!^3] M\\4\\ J\\4\\ K\\4\\ L\\4\\ 5Z 2Z #Y.Y MZ8_ HZ8_ HZ8^ FZ8^ HZ8_ HZ8_-Z8e-Q)\\ &\\-Q G\\-Q " - "G\\-Q G\\-Q 5Y Y NX Y 2[9\\ FX0X F[9[ D\\9[ E[8[ E[9[ D\\9[ 4\\ 3[9[ EZ9^ FZ9^ FZ9^ F[9^ 9b 8^9[ 8b &[2[" - " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c &Z B\\ ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :] (\\7V " - "-Z )X@X :W@TF[ BW7X?X3]6e 1X IY GX NZ GZ (X ([=[ .Z 6[ $S1^ &Z BS3^ @\\<\\ 8Z 0]9] FR6] .Z \"[ 8^ " - " ^ ;Z DW;lMc+Z$Z#Z4_ G_2Y'Z5c GZ MZ '^/\\'Z(Z :Z \"Z 4Z3] EZ 2Y JY(Y1c!^6^ HZ '^6^ LZ,Z X1] 5Y 5]6\\ :c Ab2a" - "\"Z0[ 7Z ;\\ >X @X NY MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ " - "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[ >Y X Y F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#[" - "GYH\\ DY9Y F\\FXF\\ I`;[ F\\;\\ /Z 2[=Z KZ$Z N\\ Z #^A] :n DZ(Z :Z 7]5] +X Mj (k NZ 0\\FUBP ;Z /[,[ " - "9Z X CZ 8X+W *R;R 4X+X =]:^ 1WEW LR;R /X5s.W.X GW5t(\\ 4Z$Z(Z%Z'Z$Z(Z$Y'Z$Z(Z$Z" - "8Z/Z 3_2Y NZ !Z Z !Z >Z !Z !Z \"Z :Z5c NY1c!^6^ L^6^ M^6^ M]5] M^6^ \\,[#a7^ K\\6] I\\6\\ J]6\\ J\\6] 5Z 2Z #" - "Y/Z LZ:` H[:` H[:_ FZ:` GZ:` GZ:`-[:YN\\0S(\\4Q C\\0S F\\0S F\\0S F\\0S 5Y Y NX Y 1[:[ EX0X F\\;\\ C\\;[ C[:" - "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c 'Z " - "@[ @T JT _ %] 7X.Y D^D^ BZ6Y 6b 9_ *];X -Z )X@X :ZCTH] CX7YAX1^:h 2Y JY GX NZ" - " GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[ 5[ [ 8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ MZ &`4^" - "'Z(Z :Z \"Z 4Z2] FZ 2Y JY(Y1c _:_ GZ &_9^ KZ,[![6^ 4Y 4]9] 8b @a2a#[/Z 7Z ;[ =X @X NY M[<a Fa>\\ @]7R" - " D\\=a E]4U 7Y /]Bc GY1Y 5Y NX 0X:\\ 1Y FY3Y2Y+Y1Y E\\>] E`=\\ E\\=` ;Y *U5[ 6[ /\\>a 9c :_:` GZ:Z 4` 6[ >Y " - "X Y E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J" - "Z$Z N[ NZ \"^C^ 7g @Z(Z :Z 7_9_ +X Lh &i MZ /]HUDR ;Z .Y*Y 8Z X BZ 8Y/X (Q:Q 2X/Y " - " <^<` 2WEW LQ:Q .W MV(X/X GX NW\"\\ 3Z$Z)Z#Z(Z$Z)Z#Z(Z$Z)Z#Z8Z/Z 2`7\\ NZ !Z Z !Z >Z !Z !Z \"Z :" - "Z9f MY0b _:_ J_:_ K_:_ L_9_ L_9^ N[*[$c:^ J^:^ H^:^ I^:] H]9] 4Z 2Z #YIP7[ L[<a G[<a G[=a F[<a G[<a G[<a,[=ZL\\" - "4V'\\7S B\\4V E]5V E]5V E]5V 5Y Y NX Y 1\\=\\ DX0X E\\=\\ A\\=\\ C]>] C\\=\\ A\\=\\ 3\\ 2\\=\\ C[=` E[=` E[=" - "` E[=a 8a 8`=\\ 6` #]:] H]9] G]:] G]:] H]9] 4W !a 'Z ?Z ?U KT N] $] 7X.Y Cv AZ6Z 7a 7a " - " -_?Z -Z )W?X :^GTK_ CX5XAX0_>k 3Y JX FX NZ GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX" - "@_ ,Z \"[ 3Y X 5Z CW:gJ`)Z\"Z$~T Cb=_'~W E~S FZ %b:a'Z(Z :Z \"Z 4Z1] G~Q)Y JY(Y0b N`>` FZ %a?` JZ+Z!^<a 4Y " - "3_>_ 8b @a2a$[.[ 8Z <~` AX ?X Y L\\@c Fb@] ?^<U C]Ac D^9X 7Y /aI[NY GY1Y 5Y NX 0X9\\ 2Y FY3Y2Y+Y1Y E]" - "@] Db@\\ C]Ab ;Y *X9\\ 5] 1\\Ac 9c :_:_ GZ9[ 5` 7[ =Y X Y E]DZM[ Hb@] BXB[ 8]A^ @]8T EY1Y 7_ 7Z1Y I`@" - "b#]EXLXE\\!]JYK^ CY9Y E_JXJ_ HcA] C]A] ,] 4[B\\ K~c!~T FZ 3oDo A[ :Z(Z :Z 6a?a *X Kf $g LZ .^JUGU H~U" - " JW(W 7Z X AY 7Z3Y &P9P 1Y3Y <~d 3`@b 2WEW LP9P .X MV(W/X GX MW#\\ 3Z\"Z*Z#Z)Z\"Z*" - "Z#Z)[#Z*Z#Z9Z.~T+b=_ N~T J~T I~S I~S 7Z !Z !Z \"Z :~V KY0b N`>` H`>` I`>` Ja?a Ja?` LY(Y$f?` H_>_ F_>_ G_>_ H_>" - "_ 3Z 2Z #YIS;[ K\\?c G\\?c G\\?b E\\@c F\\@c G\\?c,\\?[L^9Y'^<V B_:Y E_:Y E_:Y D^:Y 5Y Y NX Y 0]@] DX0X D]A]" - " @]@] A]@] A]A^ A]@] 2\\ 2]@] B]Ab E]Ab D\\Ab D\\Ac 7_ 7b@\\ 5` \"_@_ F_?_ E_@_ E_@_ F_?_ 3W !a 'Z ?Z " - " ?U KT M\\ #[ 6X.Y Bu AY5Z 7a 6f 2aE] -Z )W?W 9~ BW4YCY/bFp 3X KY FX NZ GZ " - ")X %^G^ >} I~S I~ $Z B| ;^F_ 7Z -aEa Dv +Z \"[ 0V U 2Z CX9dI^'Z\"Z$~S AfGd'~U C~S FZ $gGg&Z(Z :Z \"Z 4Z0] H" - "~Q)Y JY(Y0b McGd EZ $dGc IZ+[\"cEd 3Y 3cGc 7a ?`1a$Z,[ 9Z =~a AX ?X Y L^DZNY FYNZF_ =`CY B^EZNY CaB] 7" - "Y .qMY GY1Y 5Y NX 0X8\\ 3Y FY3Y2Y+Y1Y D_F_ CYNYE_ B^EZNX ;Y *]A^ 4k >^G[NY 8a 9_9^ H[8[ 5^ 6~P 2Y X Y " - " D^H[La NfH` AYD[ 6^E_ ?`?X EY1Y 7_ 7Y0Y IcFk(]HZLZI^ `Nk BY9Z E~Q GYNZE^ B_E_ ,e ;]G] J~c!~T FZ 3oDo @Z :Z(Z :" - "Z 5dGd )X Jd \"e KZ -`MUKY H~U IU&U 6Z X AY 5Z7Z LZ7Z ;~d 3cFk 8WEW " - " BW LV)X0X FW LW$\\ 2Z\"Z+[#Z)Z\"Z*Z\"Z*Z\"Z*Z\"Z:Z.~T*fGd N~T J~T I~S I~S 7Z !Z !Z \"Z :~U JY/a MdGc FcGd GcGd" - " HdGd HdGc JW&W$kGc FbFb DbFb FcFb FcGc 3Z 2Z #YIWB] I^DZNY F]D[NY F]D[NX E^DZNY F^DZNY F^E[NY+]D]J`@]&`BY AaA]" - " DaA] DaA] DaA] 5Y Y NX Y /_F_ CX0X D_E_ ?_F_ ?_F_ @_E_ ?_F_ 7aF_ @^FZMX D^FZMX D_GZMX D_G[NY 7_ 7YNYE_ 4^" - " dLd CdMd BdLd CdLd DeMd 2X !` %X =Y ?U LV MZ !Y 5X.Y As AZ4Y 6` 5~] )x -Z " - "*X@X 9} BX3YFZ-{L] 4Y LY FX NZ GZ )X $t >} I~S I} #Z B{ :v 7[ ,{ Cu *Z \"[ -S S 0Z BW8aG[%[\"Z$~R" - " ?~S'~T B~S FZ #~V%Z(Z :Z \"Z 4Z/] I~Q)Y JY(Y/a L~ DZ #~ HZ*Z\"~R 2Y 2} 5` ?`0_$[+Z 9Z =~a AX ?X Y KsN" - "Y FYNr ;u AqMY B{ 7Y -oLY GY1Y 5Y NX 0X7\\ 4Y FY3Y2Y+Y1Y Cv BYNr ArMX ;Y *y 2j >qMY 8a 8^9^ I[6Z 5^ 6~P 2Y X " - " Y CpK` N} ?YF[ 5w =x EY1Y 6] 7Z0Z J~Y(nJm M{ AY9\\ F~ FYMq @w *d ;r J~d!~T FZ 3oDo @Z :Z(Z :Z 4~ 'X " - " Ib c JZ ,u H~U HS$S 5Z X AY 4\\>\\ I]>\\ :~d 3~Y 8WEW CW KV)W0X FX LW" - "$[ 2[\"Z+Z!Z*Z\"Z+Z!Z*Z!Z,Z!Z:Z.~T)~S N~T J~T I~S I~S 7Z !Z !Z \"Z :~T IY/a L~ D~ E~ F~ E~ HU$U$~X D| B| D} D} " - "2Z 2Z #YIr HrMY FsMY FsMX DsNY ErMY FsMY+uH|%v @| C| C| C| 5Y Y NX Y .v BX0X Cw =w >v >w =w 8{ ?qMX CqMX C" - "qMX CqMY 6] 6YNr 3^ My Ay @y @z Ay 1X _ $V <X ?V LV LX NW 4X.Y @p ?Z4Z 7_ 2~[ " - " (v ,Z *X@X 9| AW1[K[+yJ] 5Y LX EX NZ GZ )X #r =} I~S I| \"Z Bz 8t 6Z *y Bt )Z \"[ *P P -Z BX" - "6[DX\"Z Z%~Q <~P&~R @~S FZ \"~T$Z(Z :Z \"Z 4Z.] J~Q)Y JY(Y/a K| CZ !{ GZ*[#~Q 1Y 1{ 4_ =_0_%[*[ :Z =~a AX >X !" - "Y JqMY FYMp 9t ApLY Az 7Y ,mKY GY1Y 5Y NX 0X6\\ 5Y FY3Y2Y+Y1Y Bt AYMp ?pLX ;Y *x 1j =oLY 8a 8]8^ IZ4Z 6" - "] 5~P 2Y X Y CoI_ N} ?[K] 3u ;w EY1Y 6] 7Y.Y JvM_'mJm Ly @Y9b K| EYLp ?u (c :p I~e\"~T FZ 3oDo @Z :Z(Z" - " :Z 2{ &X H` Ma IZ +t H~U GQ\"Q 4Z X AY 2aLb FaKa 8~d 3YNlN_ 8WEW " - "DX KV*W0o-W KW%[ 1Z Z,Z!Z+Z Z,Z!Z+Z Z,Z!Z;Z-~T'~P M~T J~T I~S I~S 7Z !Z !Z \"Z :~R GY.` K| B| C{ B{ B{ FS\"S$YM" - "{ Bz @z B{ B{ 1Z 2Z #YIq GqLY EqLY EqLX CqMY ErMY EqLY*sF{$u ?{ B{ B{ B{ 5Y Y NX Y -t AX0X Bu ;u <t <u ;u " - "8{ >pLX CpLX CpLX BoLY 6] 6YMp 1] Lv >w =v =v >w 0X _ #T ;X ?W MV LW LV 4X.Y ?n >Y3Z 7_ 1~Z " - " 't +Z *W?X 8y @X1j)vG] 5X MY EX NZ GZ *X !p <} I~S Iz Z By 6r 5Z )w As (Z \"[ " - " 5Z AX HZ Z%~ 9|$~P >~S FZ ~P\"Z(Z :Z \"Z 4Z-] K~Q)Y JY(Y.` Jy AZ x EZ)Z#~P 0Y /x 3_ =_0_%Z([ ;Z =~a AX " - ">X !Y JpLY FYLn 7s @nKY @y 7Y +kJY GY1Y 5Y NX 0X5\\ 6Y FY3Y2Y+Y1Y Ar @YLn =nKX ;Y *w /i <mKY 7_ 7]8] IZ" - "3[ 6\\ 5~P 2Y X Y BmH_ N{ <k 0r 9v EY1Y 6] 8Z.Z KYNkM_&kHk Jw ?Y8a Jy CYKn =s &b 9n H~e\"~T FZ 3oDo @Z" - " :Z(Z :Z 1y %X G^ K_ HZ *s H~U *Z X AY 1t Bs 6~d 3YNkM_ 8WEW DW " - "JV+X0o.X KW%Z 0Z Z-Z NZ,Z Z-[ Z,Z Z-[ Z<Z-~T&| K~T J~T I~S I~S 7Z !Z !Z \"Z :~P EY.` Iy @y @y @y @y DQ Q$YKy @x" - " >x ?x @y 0Z 2Z #YIp EoKY DoKY DoKX BoLY DpLY DoKY)qCy#t =y @y @y @y 5Y Y NX Y ,r @X0X As 9s :r :s 9s 7z <" - "nKX BnKX BnKX BnKY 6] 6YLn 0\\ Jt ;s :t ;t ;s .X N] !R 9V >W NX LU KU 3X.Y >l =Y2Y 7_ /~X " - " %p )Z *W?W 4u @X/i(tE] 6Y NX DX NZ GZ *X m :} I~S Iy NZ Bw 2o 5Z 'u @r 'Z \"Z " - " 4Z AY J[ Z%} 6x\"} <~S FZ N| Z(Z :Z \"Z 4Z,] L~Q)Y JY(Y.` Hv @Z Mu DZ)[$~ /Y .u 0^ =^/_&['Z ;Z =~a AX >X" - " !Y InKY FYKl 5r ?lJY >w 7Y )hIY GY1Y 5Y NX 0X4\\ 7Y FY3Y2Y+Y1Y @p ?YKl ;lJX ;Y *v -h ;kJY 7_ 7]7\\ J[2" - "[ 7\\ 5~P 2Y X Y AkE] Nz :i .p 7u EY1Y 5[ 7Y,Y KYMiL_%iGj Hu >Y8a Hv BYJl :p $a 7k H~f\"~T FZ 3oDo @Z " - ":Z(Z :Z /u #X F\\ I] GZ )r H~U *Z X AY /p >o 4~d 3YMiK^ 8WEW EX " - "JV+W/o/X JW&Z 0[ Z-Z NZ-[ [.Z NZ,Z NZ.Z NZ=Z,~T$x I~T J~T I~S I~S 7Z !Z !Z \"Z :| BY-_ Hv <v =v =u =v BXHu =v" - " <v =u <u .Z 2Z #YIo CmJY CmJY CmJX BnKY CmJY CmJY(oAx!r <x ?x ?x ?x 5Y Y NX Y +p ?X0X ?p 7p 7p 7p 7p 6WNp" - " 9lJX AlJX AlJX AlJY 5[ 5YKl /\\ Hp 8q 7p 7p 8q -X N] NP 9V ?Y X KS IS 2X.Y <h <Z2Y 6^ -~V " - " $n (Z +X@X 1o =W-f$pB] 6X NX DX Z FZ *X Nk 9} I~S Iw LZ Bv 0m 4Z %q >p %Z \"Z " - " 4Z @X JZ MZ&{ 3u z 9~S FZ Lx MZ(Z :Z \"Z 4Z+] M~Q)Y JY(Y-_ Fr >Z Lr BZ(Z!y -Y -s /] <^.]&[&[ <Z =~a AX " - " =X \"Y GjIY FYJj 2p =iIY =u 6Y 'dGY GY1Y 5Y NX 0X3\\ 8Y FY3Y2Y+Y1Y >m >YJj 8iIX ;Y *u *f :iIY 7_ 6\\7" - "\\ K[0Z 6Z 4~P 2Y X Y ?hC\\ NYMm 8f +m 3s EY1Y 5[ 8Z,Y KYLgJ^$gEh Fs =Y8a Fr @YIi 7m !` 6i G~g#~T FZ 3o" - "Do @Z :Z(Z :Z .s \"X EZ G[ FZ 'p H~U *Z X AY ,k :k 2~d 3YLgJ^ 8WEW " - " EW IV,X/o/W IW&Z 0Z MZ/[ NZ-Z MZ.Z N[.Z MZ.Z MZ>Z,~T\"t G~T J~T I~S I~S 7Z !Z !Z \"Z :y ?Y-_ Fr 8r 9r :s :r " - " AXEr :r 8r :s :s -Z 2Z #YIn AkIY BkIY BkIX @jIY BkIY BkIY'l=t Mq :t ;t ;t ;t 3Y Y NX Y *m =X0X >m 3m 5n 5m" - " 3m 6XLm 7iHX @iHX @jIX @jIY 5[ 5YJj -Z El 3k 2l 3l 4l *X N\\ 5U ?Y Y KR HQ 1X.Y 9b 9Y1Z 7" - "] )~S \"j &Z +X@X -h ;X+c!l?\\ 6X Y DX Z FZ +X Kh 8} I~S Fr JZ As ,i 3[ $n ;m " - "#Z \"Y 3Z ?X KZ MZ&x -p Mu 4~S FZ Js JZ(Z :Z \"Z 4Z*] N~Q)Y JY(Y-_ Dn <Z Jn @Z([ Nt +Y +o ,\\ ;].]&Z$" - "[ =Z =~a AX =X \"Y FhHY FYHf .m ;gHY ;p 3Y %`EY GY1Y 5Y NX 0X2\\ 9Y FY3Y2Y+Y1Y =j <YHf 5gHX ;Y (q &d 9" - "fGY 6] 5[6\\ KZ.Z 7Z 4~P 2Y X Y >gB[ NYLj 5d (j 0q EY1Y 5Z 7Y+Z LYKdG]\"dBd Bo ;Y7` Dn >YHg 4i L^ 4e " - "E~g#~T FZ 3oDo @Z :Z(Z :Z ,n NX DX EY EZ %m G~U *Z X BZ )e 4e /~d 3YKeH] 8" - "WEW FW HV,W.o0X IW'Z /Z MZ/Z LZ.Z MZ/[ MZ.Z MZ/[ MZ>Y+~T p E~T J~T I~S I~S 7Z !Z !Z \"Z :u ;Y,^ Dn 4" - "n 5n 6o 6n @XBm 5n 4n 6o 6o +Z 2Z #YIl =gGY AhGY AhGX ?hHY @hHY @gGY%i:o Hm 7p 6o 6p 7p 1Y Y NX Y (i ;X0X " - "<i 0j 1j 1j 1j 5XIi 3fGX >fGX >fGX >fGY 4Y 4YHf +Z Bg /g .g -g /g (X M[ 5T ?Z !Z JP 'X.Y 5" - "[ 6Y0Y 7] &~P Ne $Z +W?X '] 6W)a Mh<\\ 7Y !X CX Y EZ +X Id 6} I~S Cm HZ =l 'e " - "1Z i 6h !Z #Z 3Z ?Y M[ M['s &k Jo .~S FZ Gm GZ(Z :Z \"Z 4Z)] ~Q)Y JY(Y,^ Bi 9Z Gl AZ'Z Jm (Y (i )\\ " - ";].]'[#Z =Z =~a AX =X \"Y DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y" - " $k ` 7cFY 6] 5[5Z KZ-[ 8Y 3~P 2Y X Y ;b=X NYJe 0` $e +l BY1Y 4Y 7Y*Y LYIaE[ b@a >k 9Y6_ Ah ;YFc 0e " - "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i LX CV CW DZ #h D~U *Z X -R9Z #[ *[ *~d 3" - "YIaE\\ 8WEW GX HV-W-o0W HW'Z 0Z L[0Z LZ/[ LZ0Z LZ/[ LZ0Z LZ?Z+~T Lj B~T J~T I~S I~S 7Z !Z !Z \"Z :o " - "5Y,^ Ai /h 0i 0i 0i >W?i 1j 0j 1j 1i (Z 2Z #YGh 9cEY ?dEY ?dEX =dFY >dFY >cEY#d5j Ch 1j 1j 1j 1j -Y Y NX Y" - " &e 9X0X :e ,f -f -e ,f 4XFe 0cEX <bEX <bEX <bEY 4Y 4YFb )Z ?` (a '` '` (a %X 'T " - " L{ K_ 0T 4X&[ Ga AX \"Y :Y EX G_ Ie #e !_ c /a EY " - " EY Hc ?e FZ +b Ni )d Nc (X =Y #Y A^ J^ %a /] N" - "c ;Y NX *` 7YD^ ,]CX 1c ^ /Y DY X Y 8] 1YF] *\\ N` %c DY 4Y *YG\\A" - "X J\\;] 9e A^ =` 7YC] *_ G[ >a NU CZ N` 9X -T<[ " - " LYG]BX 5WEW %U HW NX MX GZ (d +b (b )b )a )b 9V;a " - ")c *c *c *c =a 4_ &^ %^ $^ &_ &_ :_/c <b +c *c *c *c 3_ K_ &` '` '_ &` 1WB_ *] $] $^ %^ NZ " - "4YD^ 'Y 6Q HQ GQ FQ HQ LX &S DQ )T 4W Q :Q" - " 9Y #X :Y EX ?Q 8R ?R @Q @R MQ =Y DY @R -Q <Z " - " #R >RM] !R <R X <X #Y ;Q <Q GR !Q @Q 2Y MX #R 0Y=Q Q=X 'Q " - " @Q *Z DY X Y ;Y <P AQ CQ ;Y 4Y *YAQ8P @Q0Q -Y 8X 7Y 3Y=Q LQ " - " JQ 4Z IU 3X -W@[ KYAQ8P 1WEW $U IV MW LW FZ " - " V KQ GR HQ GQ GQ 0T2Q HR GR HR HQ ,Q %Q GP GQ FQ GQ " - "GP *P NQ ,V MQ GR HR HR #Q =Q FQ HR HQ FQ *V:Q LQ GQ GQ GQ GY 3Y=Q !Y " - " 9X MT +X #X :Y EX " - " 5X BZ 7Y 7] 8X <X #Y " - " HY MX 0Y 'X MY CY X Y ;Y 8Y 4Y *Y 1Y E" - "X 3Y CZ IU 3X -\\I_ KY 8WEW $V" - " %Z NU 0R #V " - " )T <Z 3Y =Y 8X " - " MT +X $X 9X DX 6Y AZ NR " - " =Z 6\\ 8X <X #Y HY MX 0Y '" - "X NZ CY X X :Y 8Y 4Y *Y 1Y EX 3Y " - " CZ IU 3X -q JY 8WEW #V &Z NV " - " 0V (R " - " <Y 2Y =Y 8X MT *X " - "%X 9X EY 6X @[!T >Z 5\\ " - " 9X ;X $Y HY NY 0Y 'X NY BY X !Y " - ":Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X -p " - " IY 8WEW #V &Z MV " - " 0U 'P ;Y 2Y >Z 8X " - " MT *X &X 9X DX " - " 5X ?\\%W ?Z 4\\ :X ;X $Y " - " IZ NY 0Y 'X NY BZ !X !Y :Y 8Y 4Y *Y 1Y EX 3Y " - " CZ IU 3X -o HY 8WEW \"V " - " 'Z LU 0V " - " CZ 2Y >Y 7X " - " MT )X 'X 9X DX 5W <\\(X ?" - "Z 3\\ ;Y <X $Y IY MY 0Y 'X " - " Z AY !X !Y :Y 8Y 4Y *Y 1Y EX 3Y CZ I" - "U 3X -n GY 8WEW \"V '[3Q <V " - " 0V DY 1Y " - "?Z 7X MT )X (X 8W " - " CX 6X ;],[ AZ 1\\ <e" - " GX 2f JZ MY 0Y 'X Y @Z \"X \"Z :Y 8" - "Y 4Y *Y 1Y EX 3Y CZ IU 3X ,k " - " EY 8WEW !V 'Z4R <V " - " 0V EZ 1Y ?Y ARGX " - " MT (X )X 8W DX 5W " - " 9^1^ AZ 0\\ =e GX 2f KZ LY" - " 0Y 'X !Z @[ #X #Z 9Y 8Y 4Y *Y 1Y EX 3Y " - " CZ IU 3X )f CY 8WEW !V '[7T " - " ;V 1V " - " EY 0Y ?Y FWGW " - " LT 'W *X 8W CX 5W 8`7` A[ " - " /\\ >e GX 2f KZ LY 0Y 'X !Y >" - "\\ %X &] 9Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3" - "X $^ @Y 8WEW !V '\\:V ;V " - " 1W GZ 0Y @Z " - " FWHX LT 'X +W 7W " - " V 5b?c A[ -\\ ?e !f " - " <P2\\ MY /Y 'X \"Z >f /X 0g 9Y 8Y 4Y *Y " - " 1Y EX 3Y CZ IU 3X 5Y " - " NV &\\=X ;V " - "1W GY /Y AZ EWHX " - " LT &W ,X 7V V 3~T " - " A] ,\\ @e !f <R5\\ LY /Y " - "'X #Z =f /X 0f 8Y 8Y 4Y *Y 1Y EX 3Y " - " CZ IU 3X 5Y NW '^B[ <W " - " 1W " - " HZ /Y AZ DWIX LT &X -" - "W 6U NV 1~P B_ *\\ " - " Ae !f <U:] LZ /Y 'X #Z <e /X 0e 7Y " - " 8Y 4Y *Y 1Y EX 3Y CZ IU 3X " - " 5Y X &aJ_ <W " - " 2X IZ .Y BZ CWJY " - " LT %X /X " - " 7| Hf )\\ Be !f <X?_ N[ " - " .Y 'X %[ :d /X 0e 7Y 8Y 4Y *Y 1Y EX 3Y " - " CZ IU 3X 5Y -PDX %v " - " JQDX ?QEY " - " J[ .Y D\\ CXLY " - " KT 7x Fe " - " -_Me %b .Y 'X /e 9c /X 0c " - "5Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X " - " 5Y -d $u Je " - " ?d $d -Y Ne Ad " - " KT " - " 5s Cd ,v %b -" - "Y 'X 0e 6a /X 0b 4Y 8Y 4Y *Y 1Y EX 3Y " - " CZ IU 3X 5Y -d #t Jd " - " >d " - " %e -Y Nd @c " - " (m @c " - " +u $b -Y 'X 0d 2^ /X 0_ 1Y 8Y 4Y *Y " - " 1Y EX 3Y CZ IT 2X 5Y " - "-c !q Hd >c " - " $d ,Y Nd ?b " - " %g =" - "b *t #a ,Y 'X 0d " - " ,X /X 0Y +Y 8Y 4Y *Y 1Y EX 3Y CZ '" - "X 5Y -c Nm Fc " - " =c $c +Y Nc " - " >a " - " M\\ 8a \"~Y 1" - "r !` +Y 'X 0c 1X 1Y 8Y 4Y *Y 1Y EX 3Y " - " CZ &W 5Y -b Lj " - " Db <b " - " #b *Y Nb <_ " - " (_ " - " ~Y 1q _ *Y 'X 0b 0X 1Y " - " 8Y 4Y *Y 1Y EX 3Y CZ " - " 3Y -` He A` " - " :` !a )Y Na :] " - " " - " '] M~Y .l M] (Y 'X " - " 0` .X 1Y 8Y 4Y *Y 1Y EX 3Y " - " KY *Z B^ 9Z " - " 5Z M` (Y N` " - " 8Z " - " %X H~Y " - " *d I[ &Y 'X 0^ ,X 1Y 8Y 4Y *Y 1Y EX 3Y" - " KY " - " " - " H^ &Y N] 3V " - " " - " B~Y #X CU !X &X /Y (X 1" - "Y 7X 4X )X 0Y EX 2X " - " KY " - " HZ \"X MY " - " " - " J~Y " - " 9X " - " " - " " - " " - " 3~Y " - " 9X " - " " - " " - " " - " 3~Y " - " 9X " - " " - " " - " '" }; - - // Define a 40x38 'danger' color logo (used by cimg::dialog()). - static const unsigned char logo40x38[4576] = { - 177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200, - 1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0, - 0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200, - 1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0, - 2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255, - 255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189, - 189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189, - 189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123, - 22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200, - 1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0, - 0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1, - 123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189, - 189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255, - 0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189, - 189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255, - 255,0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2, - 123,123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0, - 1,189,189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11, - 255,255,0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0, - 1,189,189,189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11, - 255,255,0,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123, - 123,0,26,255,255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0, - 0,4,123,123,123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25, - 123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 }; - - //! Get/set default output stream for the \CImg library messages. - /** - \param file Desired output stream. Set to \c 0 to get the currently used output stream only. - \return Currently used output stream. - **/ - inline std::FILE* output(std::FILE *file) { - cimg::mutex(1); - static std::FILE *res = cimg::_stderr(); - if (file) res = file; - cimg::mutex(1,0); - return res; - } - - // Return number of available CPU cores. - inline unsigned int nb_cpus() { - unsigned int res = 1; -#if cimg_OS==2 - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - res = (unsigned int)sysinfo.dwNumberOfProcessors; -#elif cimg_OS == 1 - res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN); -#endif - return res?res:1U; - } - - // Lock/unlock mutex for CImg multi-thread programming. - inline int mutex(const unsigned int n, const int lock_mode) { - switch (lock_mode) { - case 0 : cimg::Mutex_attr().unlock(n); return 0; - case 1 : cimg::Mutex_attr().lock(n); return 0; - default : return cimg::Mutex_attr().trylock(n); - } - } - - //! Display a warning message on the default output stream. - /** - \param format C-string containing the format of the message, as with <tt>std::printf()</tt>. - \note If configuration macro \c cimg_strict_warnings is set, this function throws a - \c CImgWarningException instead. - \warning As the first argument is a format string, it is highly recommended to write - \code - cimg::warn("%s",warning_message); - \endcode - instead of - \code - cimg::warn(warning_message); - \endcode - if \c warning_message can be arbitrary, to prevent nasty memory access. - **/ - inline void warn(const char *const format, ...) { - if (cimg::exception_mode()>=1) { - char *const message = new char[16384]; - std::va_list ap; - va_start(ap,format); - cimg_vsnprintf(message,16384,format,ap); - va_end(ap); -#ifdef cimg_strict_warnings - throw CImgWarningException(message); -#else - std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message); -#endif - delete[] message; - } - } - - // Execute an external system command. - /** - \param command C-string containing the command line to execute. - \param module_name Module name. - \return Status value of the executed command, whose meaning is OS-dependent. - \note This function is similar to <tt>std::system()</tt> - but it does not open an extra console windows - on Windows-based systems. - **/ - inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) { - cimg::unused(module_name); -#ifdef cimg_no_system_calls - return -1; -#else - if (is_verbose) return std::system(command); -#if cimg_OS==1 - const unsigned int l = (unsigned int)std::strlen(command); - if (l) { - char *const ncommand = new char[l + 24]; - std::memcpy(ncommand,command,l); - std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent - const int out_val = std::system(ncommand); - delete[] ncommand; - return out_val; - } else return -1; -#elif cimg_OS==2 - PROCESS_INFORMATION pi; - STARTUPINFO si; - std::memset(&pi,0,sizeof(PROCESS_INFORMATION)); - std::memset(&si,0,sizeof(STARTUPINFO)); - GetStartupInfo(&si); - si.cb = sizeof(si); - si.wShowWindow = SW_HIDE; - si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW; - const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi); - if (res) { - WaitForSingleObject(pi.hProcess,INFINITE); - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - return 0; - } else return std::system(command); -#else - return std::system(command); -#endif -#endif - } - - //! Return a reference to a temporary variable of type T. - template<typename T> - inline T& temporary(const T&) { - static T temp; - return temp; - } - - //! Exchange values of variables \c a and \c b. - template<typename T> - inline void swap(T& a, T& b) { T t = a; a = b; b = t; } - - //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2). - template<typename T1, typename T2> - inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { - cimg::swap(a1,b1); cimg::swap(a2,b2); - } - - //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3). - template<typename T1, typename T2, typename T3> - inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) { - cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); - } - - //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4). - template<typename T1, typename T2, typename T3, typename T4> - inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) { - cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); - } - - //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5). - template<typename T1, typename T2, typename T3, typename T4, typename T5> - inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) { - cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); - } - - //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6). - template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> - inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) { - cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); - } - - //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7). - template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> - inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, - T7& a7, T7& b7) { - cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); - } - - //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8). - template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> - inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, - T7& a7, T7& b7, T8& a8, T8& b8) { - cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); - } - - //! Return the endianness of the current architecture. - /** - \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>. - **/ - inline bool endianness() { - const int x = 1; - return ((unsigned char*)&x)[0]?false:true; - } - - //! Reverse endianness of all elements in a memory buffer. - /** - \param[in,out] buffer Memory buffer whose endianness must be reversed. - \param size Number of buffer elements to reverse. - **/ - template<typename T> - inline void invert_endianness(T* const buffer, const cimg_ulong size) { - if (size) switch (sizeof(T)) { - case 1 : break; - case 2 : { - for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) { - const unsigned short val = *(--ptr); - *ptr = (unsigned short)((val>>8) | ((val<<8))); - } - } break; - case 4 : { - for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) { - const unsigned int val = *(--ptr); - *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24); - } - } break; - case 8 : { - const cimg_uint64 - m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24, - m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56; - for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) { - const cimg_uint64 val = *(--ptr); - *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) | - ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56)); - } - } break; - default : { - for (T* ptr = buffer + size; ptr>buffer; ) { - unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); - for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); - } - } - } - } - - //! Reverse endianness of a single variable. - /** - \param[in,out] a Variable to reverse. - \return Reference to reversed variable. - **/ - template<typename T> - inline T& invert_endianness(T& a) { - invert_endianness(&a,1); - return a; - } - - // Conversion functions to get more precision when trying to store unsigned ints values as floats. - inline unsigned int float2uint(const float f) { - int tmp = 0; - std::memcpy(&tmp,&f,sizeof(float)); - if (tmp>=0) return (unsigned int)f; - unsigned int u; - // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler. - std::memcpy(&u,&f,sizeof(float)); - return ((u)<<1)>>1; // set sign bit to 0 - } - - inline float uint2float(const unsigned int u) { - if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287) - float f; - const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1 - // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. - std::memcpy(&f,&v,sizeof(float)); - return f; - } - - //! Return the value of a system timer, with a millisecond precision. - /** - \note The timer does not necessarily starts from \c 0. - **/ - inline cimg_ulong time() { -#if cimg_OS==1 - struct timeval st_time; - gettimeofday(&st_time,0); - return (cimg_ulong)(st_time.tv_usec/1000 + st_time.tv_sec*1000); -#elif cimg_OS==2 - SYSTEMTIME st_time; - GetLocalTime(&st_time); - return (cimg_ulong)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); -#else - return 0; -#endif - } - - // Implement a tic/toc mechanism to display elapsed time of algorithms. - inline cimg_ulong tictoc(const bool is_tic); - - //! Start tic/toc timer for time measurement between code instructions. - /** - \return Current value of the timer (same value as time()). - **/ - inline cimg_ulong tic() { - return cimg::tictoc(true); - } - - //! End tic/toc timer and displays elapsed time from last call to tic(). - /** - \return Time elapsed (in ms) since last call to tic(). - **/ - inline cimg_ulong toc() { - return cimg::tictoc(false); - } - - //! Sleep for a given numbers of milliseconds. - /** - \param milliseconds Number of milliseconds to wait for. - \note This function frees the CPU ressources during the sleeping time. - It can be used to temporize your program properly, without wasting CPU time. - **/ - inline void sleep(const unsigned int milliseconds) { -#if cimg_OS==1 - struct timespec tv; - tv.tv_sec = milliseconds/1000; - tv.tv_nsec = (milliseconds%1000)*1000000; - nanosleep(&tv,0); -#elif cimg_OS==2 - Sleep(milliseconds); -#else - cimg::unused(milliseconds); -#endif - } - - inline unsigned int wait(const unsigned int milliseconds, cimg_ulong *const p_timer) { - if (!*p_timer) *p_timer = cimg::time(); - const cimg_ulong current_time = cimg::time(); - if (current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; } - const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time); - *p_timer = current_time + time_diff; - cimg::sleep(time_diff); - return time_diff; - } - - //! Wait for a given number of milliseconds since the last call to wait(). - /** - \param milliseconds Number of milliseconds to wait for. - \return Number of milliseconds elapsed since the last call to wait(). - \note Same as sleep() with a waiting time computed with regard to the last call - of wait(). It may be used to temporize your program properly, without wasting CPU time. - **/ - inline cimg_long wait(const unsigned int milliseconds) { - cimg::mutex(3); - static cimg_ulong timer = cimg::time(); - cimg::mutex(3,0); - return cimg::wait(milliseconds,&timer); - } - - // Custom random number generator (allow re-entrance). - inline cimg_ulong& rng() { // Used as a shared global number for rng - static cimg_ulong rng = 0xB16B00B5U; - return rng; - } - - inline unsigned int _rand(cimg_ulong *const p_rng) { - *p_rng = *p_rng*1103515245 + 12345U; - return (unsigned int)*p_rng; - } - - inline unsigned int _rand() { - cimg::mutex(4); - const unsigned int res = cimg::_rand(&cimg::rng()); - cimg::mutex(4,0); - return res; - } - - inline void srand(cimg_ulong *const p_rng) { -#if cimg_OS==1 - *p_rng = cimg::time() + (cimg_ulong)getpid(); -#elif cimg_OS==2 - *p_rng = cimg::time() + (cimg_ulong)_getpid(); -#endif - } - - inline void srand() { - cimg::mutex(4); - cimg::srand(&cimg::rng()); - cimg::mutex(4,0); - } - - inline void srand(const cimg_ulong seed) { - cimg::mutex(4); - cimg::rng() = seed; - cimg::mutex(4,0); - } - - inline double rand(const double val_min, const double val_max, cimg_ulong *const p_rng) { - const double val = cimg::_rand(p_rng)/(double)~0U; - return val_min + (val_max - val_min)*val; - } - - inline double rand(const double val_min, const double val_max) { - cimg::mutex(4); - const double res = cimg::rand(val_min,val_max,&cimg::rng()); - cimg::mutex(4,0); - return res; - } - - inline double rand(const double val_max, cimg_ulong *const p_rng) { - const double val = cimg::_rand(p_rng)/(double)~0U; - return val_max*val; - } - - inline double rand(const double val_max=1) { - cimg::mutex(4); - const double res = cimg::rand(val_max,&cimg::rng()); - cimg::mutex(4,0); - return res; - } - - inline double grand(cimg_ulong *const p_rng) { - double x1, w; - do { - const double x2 = cimg::rand(-1,1,p_rng); - x1 = cimg::rand(-1,1,p_rng); - w = x1*x1 + x2*x2; - } while (w<=0 || w>=1.); - return x1*std::sqrt((-2*std::log(w))/w); - } - - inline double grand() { - cimg::mutex(4); - const double res = cimg::grand(&cimg::rng()); - cimg::mutex(4,0); - return res; - } - - inline unsigned int prand(const double z, cimg_ulong *const p_rng) { - if (z<=1.e-10) return 0; - if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z); - unsigned int k = 0; - const double y = std::exp(-z); - for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng); - return k - 1; - } - - inline unsigned int prand(const double z) { - cimg::mutex(4); - const unsigned int res = cimg::prand(z,&cimg::rng()); - cimg::mutex(4,0); - return res; - } - - //! Cut (i.e. clamp) value in specified interval. - template<typename T, typename t> - inline T cut(const T& val, const t& val_min, const t& val_max) { - return val<val_min?(T)val_min:val>val_max?(T)val_max:val; - } - - //! Bitwise-rotate value on the left. - template<typename T> - inline T rol(const T& a, const unsigned int n=1) { - return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a; - } - - inline float rol(const float a, const unsigned int n=1) { - return (float)rol((int)a,n); - } - - inline double rol(const double a, const unsigned int n=1) { - return (double)rol((cimg_long)a,n); - } - - inline double rol(const long double a, const unsigned int n=1) { - return (double)rol((cimg_long)a,n); - } - -#ifdef cimg_use_half - inline half rol(const half a, const unsigned int n=1) { - return (half)rol((int)a,n); - } -#endif - - //! Bitwise-rotate value on the right. - template<typename T> - inline T ror(const T& a, const unsigned int n=1) { - return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a; - } - - inline float ror(const float a, const unsigned int n=1) { - return (float)ror((int)a,n); - } - - inline double ror(const double a, const unsigned int n=1) { - return (double)ror((cimg_long)a,n); - } - - inline double ror(const long double a, const unsigned int n=1) { - return (double)ror((cimg_long)a,n); - } - -#ifdef cimg_use_half - inline half ror(const half a, const unsigned int n=1) { - return (half)ror((int)a,n); - } -#endif - - //! Return absolute value of a value. - template<typename T> - inline T abs(const T& a) { - return a>=0?a:-a; - } - inline bool abs(const bool a) { - return a; - } - inline int abs(const unsigned char a) { - return (int)a; - } - inline int abs(const unsigned short a) { - return (int)a; - } - inline int abs(const unsigned int a) { - return (int)a; - } - inline int abs(const int a) { - return std::abs(a); - } - inline cimg_int64 abs(const cimg_uint64 a) { - return (cimg_int64)a; - } - inline double abs(const double a) { - return std::fabs(a); - } - inline float abs(const float a) { - return (float)std::fabs((double)a); - } - - //! Return hyperbolic arcosine of a value. - inline double acosh(const double x) { -#if cimg_use_cpp11==1 && !defined(_MSC_VER) - return std::acosh(x); -#else - return std::log(x + std::sqrt(x*x - 1)); -#endif - } - - //! Return hyperbolic arcsine of a value. - inline double asinh(const double x) { -#if cimg_use_cpp11==1 && !defined(_MSC_VER) - return std::asinh(x); -#else - return std::log(x + std::sqrt(x*x + 1)); -#endif - } - - //! Return hyperbolic arctangent of a value. - inline double atanh(const double x) { -#if cimg_use_cpp11==1 && !defined(_MSC_VER) - return std::atanh(x); -#else - return 0.5*std::log((1. + x)/(1. - x)); -#endif - } - - //! Return the sinc of a given value. - inline double sinc(const double x) { - return x?std::sin(x)/x:1; - } - - //! Return base-2 logarithm of a value. - inline double log2(const double x) { -#if cimg_use_cpp11==1 && !defined(_MSC_VER) - return std::log2(x); -#else - const double base2 = std::log(2.); - return std::log(x)/base2; -#endif - } - - //! Return square of a value. - template<typename T> - inline T sqr(const T& val) { - return val*val; - } - - //! Return cubic root of a value. - template<typename T> - inline double cbrt(const T& x) { -#if cimg_use_cpp11==1 - return std::cbrt(x); -#else - return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3); -#endif - } - - template<typename T> - inline T pow3(const T& val) { - return val*val*val; - } - template<typename T> - inline T pow4(const T& val) { - return val*val*val*val; - } - - //! Return the minimum between three values. - template<typename t> - inline t min(const t& a, const t& b, const t& c) { - return std::min(std::min(a,b),c); - } - - //! Return the minimum between four values. - template<typename t> - inline t min(const t& a, const t& b, const t& c, const t& d) { - return std::min(std::min(a,b),std::min(c,d)); - } - - //! Return the maximum between three values. - template<typename t> - inline t max(const t& a, const t& b, const t& c) { - return std::max(std::max(a,b),c); - } - - //! Return the maximum between four values. - template<typename t> - inline t max(const t& a, const t& b, const t& c, const t& d) { - return std::max(std::max(a,b),std::max(c,d)); - } - - //! Return the sign of a value. - template<typename T> - inline T sign(const T& x) { - return (T)(x<0?-1:x>0); - } - - //! Return the nearest power of 2 higher than given value. - template<typename T> - inline cimg_ulong nearest_pow2(const T& x) { - cimg_ulong i = 1; - while (x>i) i<<=1; - return i; - } - - //! Return the modulo of a value. - /** - \param x Input value. - \param m Modulo value. - \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type. - **/ - template<typename T> - inline T mod(const T& x, const T& m) { - const double dx = (double)x, dm = (double)m; - return (T)(dx - dm * std::floor(dx / dm)); - } - inline int mod(const bool x, const bool m) { - return m?(x?1:0):0; - } - inline int mod(const unsigned char x, const unsigned char m) { - return x%m; - } - inline int mod(const char x, const char m) { -#if defined(CHAR_MAX) && CHAR_MAX==255 - return x%m; -#else - return x>=0?x%m:(x%m?m + x%m:0); -#endif - } - inline int mod(const unsigned short x, const unsigned short m) { - return x%m; - } - inline int mod(const short x, const short m) { - return x>=0?x%m:(x%m?m + x%m:0); - } - inline int mod(const unsigned int x, const unsigned int m) { - return (int)(x%m); - } - inline int mod(const int x, const int m) { - return x>=0?x%m:(x%m?m + x%m:0); - } - inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) { - return x%m; - } - inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) { - return x>=0?x%m:(x%m?m + x%m:0); - } - - //! Return the min-mod of two values. - /** - \note <i>minmod(\p a,\p b)</i> is defined to be: - - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign. - - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs. - **/ - template<typename T> - inline T minmod(const T& a, const T& b) { - return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a)); - } - - template<typename T> - inline T round(const T& x) { - return (T)std::floor((_cimg_Tfloat)x + 0.5f); - } - - //! Return rounded value. - /** - \param x Value to be rounded. - \param y Rounding precision. - \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward). - \return Rounded value, having the same type as input value \c x. - **/ - template<typename T> - inline T round(const T& x, const double y, const int rounding_type=0) { - if (y<=0) return x; - if (y==1) switch (rounding_type) { - case 0 : return cimg::round(x); - case 1 : return (T)std::ceil((_cimg_Tfloat)x); - default : return (T)std::floor((_cimg_Tfloat)x); - } - const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor; - return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx))); - } - - // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values. - // (contribution by RawTherapee: http://rawtherapee.com/). - template<typename T> - inline T median(T val0, T val1) { - return (val0 + val1)/2; - } - - template<typename T> - inline T median(T val0, T val1, T val2) { - return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1))); - } - - template<typename T> - inline T median(T val0, T val1, T val2, T val3, T val4) { - T tmp = std::min(val0,val1); - val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); - val3 = std::max(val0,tmp); val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2); - val1 = tmp; tmp = std::min(val2,val3); - return std::max(val1,tmp); - } - - template<typename T> - inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) { - T tmp = std::min(val0,val5); - val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp; - tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4); - val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5); - val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); - val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp); - tmp = std::min(val1,tmp); val3 = std::max(tmp,val3); - return std::min(val3,val4); - } - - template<typename T> - inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) { - T tmp = std::min(val1,val2); - val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); - val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); - val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1); - val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); - val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7); - val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2); - val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); - val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); - val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8); - val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6); - val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7); - tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp); - return std::min(val4,val2); - } - - template<typename T> - inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8, T val9, T val10, T val11, - T val12) { - T tmp = std::min(val1,val7); - val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; - tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8); - val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12); - val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1); - val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp; - tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11); - val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp; - tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2); - val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; - tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4); - val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; - tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12); - tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10); - val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; - tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9); - val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp; - tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3); - tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10); - val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8); - val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp); - val5 = std::max(tmp,val5); val6 = std::min(val6,val7); - return std::max(val5,val6); - } - - template<typename T> - inline T median(T val0, T val1, T val2, T val3, T val4, - T val5, T val6, T val7, T val8, T val9, - T val10, T val11, T val12, T val13, T val14, - T val15, T val16, T val17, T val18, T val19, - T val20, T val21, T val22, T val23, T val24) { - T tmp = std::min(val0,val1); - val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); - val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3); - val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; - tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6); - tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10); - val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9); - tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13); - val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12); - tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16); - val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15); - tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19); - val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18); - tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22); - val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21); - tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5); - val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp; - tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3); - tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7); - val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14); - val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14); - val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15); - val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15); - val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16); - val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16); - val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23); - val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23); - val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24); - val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24); - val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22); - val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18); - val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18); - val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp; - tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10); - val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp; - tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11); - tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21); - val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12); - tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22); - val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23); - val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23); - val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24); - val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15); - val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19); - tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp); - val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11); - val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17); - tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12); - val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14); - val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7); - tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14); - tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12); - val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp); - val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp; - val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp; - tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp); - val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp; - tmp = std::min(val10,val20); - return std::max(tmp,val12); - } - - template<typename T> - inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, - T val7, T val8, T val9, T val10, T val11, T val12, T val13, - T val14, T val15, T val16, T val17, T val18, T val19, T val20, - T val21, T val22, T val23, T val24, T val25, T val26, T val27, - T val28, T val29, T val30, T val31, T val32, T val33, T val34, - T val35, T val36, T val37, T val38, T val39, T val40, T val41, - T val42, T val43, T val44, T val45, T val46, T val47, T val48) { - T tmp = std::min(val0,val32); - val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp; - tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35); - val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp; - tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38); - val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp; - tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41); - val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42); - val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp; - tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45); - val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46); - val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp; - tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16); - val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17); - val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19); - val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp; - tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22); - val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp; - tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25); - val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26); - val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp; - tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29); - val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30); - val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp; - tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32); - val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33); - val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp; - tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36); - val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37); - val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp; - tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40); - val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41); - val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp; - tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44); - val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45); - val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp; - tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8); - val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp; - tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11); - val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp; - tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14); - val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp; - tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25); - val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26); - val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp; - tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29); - val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30); - val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp; - tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41); - val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42); - val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp; - tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45); - val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46); - val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp; - tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33); - val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34); - val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp; - tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37); - val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38); - val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp; - tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16); - val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17); - val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp; - tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20); - val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21); - val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp; - tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32); - val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33); - val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp; - tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36); - val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37); - val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp; - tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48); - val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4); - val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6); - val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; - tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13); - val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14); - val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp; - tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21); - val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22); - val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp; - tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29); - val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30); - val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp; - tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37); - val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38); - val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp; - tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45); - val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46); - val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp; - tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33); - val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34); - val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp; - tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41); - val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42); - val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp; - tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16); - val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17); - val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp; - tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24); - val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25); - val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp; - tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32); - val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33); - val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp; - tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40); - val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41); - val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp; - tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48); - val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8); - val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10); - val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp; - tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17); - val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18); - val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp; - tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25); - val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26); - val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp; - tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33); - val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34); - val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp; - tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41); - val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42); - val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp; - tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2); - val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp; - tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7); - val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp; - tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14); - val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15); - val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp; - tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22); - val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23); - val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp; - tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30); - val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31); - val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp; - tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38); - val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39); - val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp; - tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46); - val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47); - val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33); - val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp; - tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40); - val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41); - val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp; - tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48); - val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16); - val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp; - tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21); - val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24); - val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp; - tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29); - val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32); - val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp; - tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37); - val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40); - val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp; - tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45); - val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48); - val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9); - val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; - tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16); - val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17); - val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp; - tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24); - val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25); - val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp; - tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32); - val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33); - val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp; - tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40); - val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41); - val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp; - tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48); - val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); - val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8); - val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp; - tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13); - val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16); - val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp; - tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21); - val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24); - val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp; - tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29); - val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32); - val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp; - tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37); - val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40); - val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp; - tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45); - val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48); - val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5); - val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11); - val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17); - val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23); - val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29); - val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35); - val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41); - val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47); - val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36); - val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42); - val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48); - val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28); - val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34); - val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24); - val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30); - val24 = std::max(val21,val24); val23 = std::min(val23,val26); - return std::max(val23,val24); - } - - //! Return sqrt(x^2 + y^2). - template<typename T> - inline T hypot(const T x, const T y) { - return std::sqrt(x*x + y*y); - } - - template<typename T> - inline T hypot(const T x, const T y, const T z) { - return std::sqrt(x*x + y*y + z*z); - } - - template<typename T> - inline T _hypot(const T x, const T y) { // Slower but more precise version - T nx = cimg::abs(x), ny = cimg::abs(y), t; - if (nx<ny) { t = nx; nx = ny; } else t = ny; - if (nx>0) { t/=nx; return nx*std::sqrt(1 + t*t); } - return 0; - } - - //! Return the factorial of n - inline double factorial(const int n) { - if (n<0) return cimg::type<double>::nan(); - if (n<2) return 1; - double res = 2; - for (int i = 3; i<=n; ++i) res*=i; - return res; - } - - //! Return the number of permutations of k objects in a set of n objects. - inline double permutations(const int k, const int n, const bool with_order) { - if (n<0 || k<0) return cimg::type<double>::nan(); - if (k>n) return 0; - double res = 1; - for (int i = n; i>=n - k + 1; --i) res*=i; - return with_order?res:res/cimg::factorial(k); - } - - inline double _fibonacci(int exp) { - double - base = (1 + std::sqrt(5.))/2, - result = 1/std::sqrt(5.); - while (exp) { - if (exp&1) result*=base; - exp>>=1; - base*=base; - } - return result; - } - - //! Calculate fibonacci number. - // (Precise up to n = 78, less precise for n>78). - inline double fibonacci(const int n) { - if (n<0) return cimg::type<double>::nan(); - if (n<3) return 1; - if (n<11) { - cimg_uint64 fn1 = 1, fn2 = 1, fn = 0; - for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } - return (double)fn; - } - if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10 - return (double)((cimg_uint64)(_fibonacci(n) + 0.5)); - - if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93 - cimg_uint64 - fn1 = (cimg_uint64)1304969544928657ULL, - fn2 = (cimg_uint64)806515533049393ULL, - fn = 0; - for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } - return (double)fn; - } - return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation - } - - //! Calculate greatest common divisor. - inline long gcd(long a, long b) { - while (a) { const long c = a; a = b%a; b = c; } - return b; - } - - //! Convert Ascii character to lower case. - inline char lowercase(const char x) { - return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a'); - } - inline double lowercase(const double x) { - return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a'); - } - - //! Convert C-string to lower case. - inline void lowercase(char *const str) { - if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr); - } - - //! Convert Ascii character to upper case. - inline char uppercase(const char x) { - return (char)((x<'a'||x>'z')?x:x - 'a' + 'A'); - } - - inline double uppercase(const double x) { - return (double)((x<'a'||x>'z')?x:x - 'a' + 'A'); - } - - //! Convert C-string to upper case. - inline void uppercase(char *const str) { - if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr); - } - - //! Return \c true if input character is blank (space, tab, or non-printable character). - inline bool is_blank(const char c) { - return c>=0 && c<=' '; - } - - //! Read value in a C-string. - /** - \param str C-string containing the float value to read. - \return Read value. - \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings, - as in <em>"1/2"</em>. - **/ - inline double atof(const char *const str) { - double x = 0, y = 1; - return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; - } - - //! Compare the first \p l characters of two C-strings, ignoring the case. - /** - \param str1 C-string. - \param str2 C-string. - \param l Number of characters to compare. - \return \c 0 if the two strings are equal, something else otherwise. - \note This function has to be defined since it is not provided by all C++-compilers (not ANSI). - **/ - inline int strncasecmp(const char *const str1, const char *const str2, const int l) { - if (!l) return 0; - if (!str1) return str2?-1:0; - const char *nstr1 = str1, *nstr2 = str2; - int k, diff = 0; for (k = 0; k<l && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; } - return k!=l?diff:0; - } - - //! Compare two C-strings, ignoring the case. - /** - \param str1 C-string. - \param str2 C-string. - \return \c 0 if the two strings are equal, something else otherwise. - \note This function has to be defined since it is not provided by all C++-compilers (not ANSI). - **/ - inline int strcasecmp(const char *const str1, const char *const str2) { - if (!str1) return str2?-1:0; - const int - l1 = (int)std::strlen(str1), - l2 = (int)std::strlen(str2); - return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2)); - } - - //! Ellipsize a string. - /** - \param str C-string. - \param l Max number of characters. - \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string. - **/ - inline char *strellipsize(char *const str, const unsigned int l=64, - const bool is_ending=true) { - if (!str) return str; - const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str); - if (ls<=nl) return str; - if (is_ending) std::strcpy(str + nl - 5,"(...)"); - else { - const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5; - std::strcpy(str + ll,"(...)"); - std::memmove(str + ll + 5,str + ls - lr,lr); - } - str[nl] = 0; - return str; - } - - //! Ellipsize a string. - /** - \param str C-string. - \param res output C-string. - \param l Max number of characters. - \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string. - **/ - inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64, - const bool is_ending=true) { - const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str); - if (ls<=nl) { std::strcpy(res,str); return res; } - if (is_ending) { - std::strncpy(res,str,nl - 5); - std::strcpy(res + nl -5,"(...)"); - } else { - const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5; - std::strncpy(res,str,ll); - std::strcpy(res + ll,"(...)"); - std::strncpy(res + ll + 5,str + ls - lr,lr); - } - res[nl] = 0; - return res; - } - - //! Remove delimiters on the start and/or end of a C-string. - /** - \param[in,out] str C-string to work with (modified at output). - \param delimiter Delimiter character code to remove. - \param is_symmetric Tells if the removal is done only if delimiters are symmetric - (both at the beginning and the end of \c s). - \param is_iterative Tells if the removal is done if several iterations are possible. - \return \c true if delimiters have been removed, \c false otherwise. - **/ - inline bool strpare(char *const str, const char delimiter, - const bool is_symmetric, const bool is_iterative) { - if (!str) return false; - const int l = (int)std::strlen(str); - int p, q; - if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) { - --q; ++p; if (!is_iterative) break; - } else { - for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; } - for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; } - } - const int n = q - p + 1; - if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } - return false; - } - - //! Remove white spaces on the start and/or end of a C-string. - inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) { - if (!str) return false; - const int l = (int)std::strlen(str); - int p, q; - if (is_symmetric) for (p = 0, q = l - 1; p<q && is_blank(str[p]) && is_blank(str[q]); ) { - --q; ++p; if (!is_iterative) break; - } else { - for (p = 0; p<l && is_blank(str[p]); ) { ++p; if (!is_iterative) break; } - for (q = l - 1; q>p && is_blank(str[q]); ) { --q; if (!is_iterative) break; } - } - const int n = q - p + 1; - if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } - return false; - } - - //! Replace reserved characters (for Windows filename) by another character. - /** - \param[in,out] str C-string to work with (modified at output). - \param[in] c Replacement character. - **/ - inline void strwindows_reserved(char *const str, const char c='_') { - for (char *s = str; *s; ++s) { - const char i = *s; - if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c; - } - } - - //! Replace escape sequences in C-strings by their binary Ascii values. - /** - \param[in,out] str C-string to work with (modified at output). - **/ - inline void strunescape(char *const str) { -#define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break; - unsigned int val = 0; - for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) { - cimg_strunescape('a','\a'); - cimg_strunescape('b','\b'); - cimg_strunescape('e',0x1B); - cimg_strunescape('f','\f'); - cimg_strunescape('n','\n'); - cimg_strunescape('r','\r'); - cimg_strunescape('t','\t'); - cimg_strunescape('v','\v'); - cimg_strunescape('\\','\\'); - cimg_strunescape('\'','\''); - cimg_strunescape('\"','\"'); - cimg_strunescape('\?','\?'); - case 0 : *nd = 0; break; - case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : - cimg_sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns; - *nd = (char)val; break; - case 'x' : - cimg_sscanf(++ns,"%x",&val); - while ((*ns>='0' && *ns<='9') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns; - *nd = (char)val; break; - default : *nd = *(ns++); - } else *nd = *(ns++); - } - - // Return a temporary string describing the size of a memory buffer. - inline const char *strbuffersize(const cimg_ulong size); - - // Return string that identifies the running OS. - inline const char *stros() { -#if defined(linux) || defined(__linux) || defined(__linux__) - static const char *const str = "Linux"; -#elif defined(sun) || defined(__sun) - static const char *const str = "Sun OS"; -#elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) - static const char *const str = "BSD"; -#elif defined(sgi) || defined(__sgi) - static const char *const str = "Irix"; -#elif defined(__MACOSX__) || defined(__APPLE__) - static const char *const str = "Mac OS"; -#elif defined(unix) || defined(__unix) || defined(__unix__) - static const char *const str = "Generic Unix"; -#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ - defined(WIN64) || defined(_WIN64) || defined(__WIN64__) - static const char *const str = "Windows"; -#else - const char - *const _str1 = std::getenv("OSTYPE"), - *const _str2 = _str1?_str1:std::getenv("OS"), - *const str = _str2?_str2:"Unknown OS"; -#endif - return str; - } - - //! Return the basename of a filename. - inline const char* basename(const char *const s, const char separator=cimg_file_separator) { - const char *p = 0, *np = s; - while (np>=s && (p=np)) np = std::strchr(np,separator) + 1; - return p; - } - - // Return a random filename. - inline const char* filenamerand() { - cimg::mutex(6); - static char randomid[9]; - for (unsigned int k = 0; k<8; ++k) { - const int v = (int)cimg::rand(65535)%3; - randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)): - (v==1?('a' + ((int)cimg::rand(65535)%26)): - ('A' + ((int)cimg::rand(65535)%26)))); - } - cimg::mutex(6,0); - return randomid; - } - - // Convert filename as a Windows-style filename (short path name). - inline void winformat_string(char *const str) { - if (str && *str) { -#if cimg_OS==2 - char *const nstr = new char[MAX_PATH]; - if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr); - delete[] nstr; -#endif - } - } - - // Open a file (similar to std:: fopen(), but with wide character support on Windows). - inline std::FILE *std_fopen(const char *const path, const char *const mode); - - - //! Open a file. - /** - \param path Path of the filename to open. - \param mode C-string describing the opening mode. - \return Opened file. - \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when - the specified file cannot be opened, instead of returning \c 0. - **/ - inline std::FILE *fopen(const char *const path, const char *const mode) { - if (!path) - throw CImgArgumentException("cimg::fopen(): Specified file path is (null)."); - if (!mode) - throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).", - path); - std::FILE *res = 0; - if (*path=='-' && (!path[1] || path[1]=='.')) { - res = (*mode=='r')?cimg::_stdin():cimg::_stdout(); -#if cimg_OS==2 - if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode -#ifdef __BORLANDC__ - if (setmode(_fileno(res),0x8000)==-1) res = 0; -#else - if (_setmode(_fileno(res),0x8000)==-1) res = 0; -#endif - } -#endif - } else res = cimg::std_fopen(path,mode); - if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.", - path,mode); - return res; - } - - //! Close a file. - /** - \param file File to close. - \return \c 0 if file has been closed properly, something else otherwise. - \note Same as <tt>std::fclose()</tt> but display a warning message if - the file has not been closed properly. - **/ - inline int fclose(std::FILE *file) { - if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; } - if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0; - const int errn = std::fclose(file); - if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.", - errn); - return errn; - } - - //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows). - inline int fseek(FILE *stream, cimg_long offset, int origin) { -#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) - return _fseeki64(stream,(__int64)offset,origin); -#else - return std::fseek(stream,offset,origin); -#endif - } - - //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows). - inline cimg_long ftell(FILE *stream) { -#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) - return (cimg_long)_ftelli64(stream); -#else - return (cimg_long)std::ftell(stream); -#endif - } - - //! Check if a path is a directory. - /** - \param path Specified path to test. - **/ - inline bool is_directory(const char *const path) { - if (!path || !*path) return false; -#if cimg_OS==1 - struct stat st_buf; - return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode)); -#elif cimg_OS==2 - const unsigned int res = (unsigned int)GetFileAttributesA(path); - return res==INVALID_FILE_ATTRIBUTES?false:(res&16); -#else - return false; -#endif - } - - //! Check if a path is a file. - /** - \param path Specified path to test. - **/ - inline bool is_file(const char *const path) { - if (!path || !*path) return false; - std::FILE *const file = cimg::std_fopen(path,"rb"); - if (!file) return false; - cimg::fclose(file); - return !is_directory(path); - } - - //! Get file size. - /** - \param filename Specified filename to get size from. - \return File size or '-1' if file does not exist. - **/ - inline cimg_int64 fsize(const char *const filename) { - std::FILE *const file = cimg::std_fopen(filename,"rb"); - if (!file) return (cimg_int64)-1; - std::fseek(file,0,SEEK_END); - const cimg_int64 siz = (cimg_int64)std::ftell(file); - cimg::fclose(file); - return siz; - } - - //! Get last write time of a given file or directory (multiple-attributes version). - /** - \param path Specified path to get attributes from. - \param[in,out] attr Type of requested time attributes. - Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } - Replaced by read attributes after return (or -1 if an error occured). - \param nb_attr Number of attributes to read/write. - \return Latest read attribute. - **/ - template<typename T> - inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) { -#define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1 - int res = -1; - if (!path || !*path) { _cimg_fdate_err(); return -1; } - cimg::mutex(6); -#if cimg_OS==2 - HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); - if (file!=INVALID_HANDLE_VALUE) { - FILETIME _ft; - SYSTEMTIME ft; - if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) { - for (unsigned int i = 0; i<nb_attr; ++i) { - res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay: - attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute: - attr[i]==6?ft.wSecond:-1); - attr[i] = (T)res; - } - } else _cimg_fdate_err(); - CloseHandle(file); - } else _cimg_fdate_err(); -#elif cimg_OS==1 - struct stat st_buf; - if (!stat(path,&st_buf)) { - const time_t _ft = st_buf.st_mtime; - const struct tm& ft = *std::localtime(&_ft); - for (unsigned int i = 0; i<nb_attr; ++i) { - res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday: - attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min: - attr[i]==6?ft.tm_sec:-1); - attr[i] = (T)res; - } - } else _cimg_fdate_err(); -#endif - cimg::mutex(6,0); - return res; - } - - //! Get last write time of a given file or directory (single-attribute version). - /** - \param path Specified path to get attributes from. - \param attr Type of requested time attributes. - Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } - \return Specified attribute or -1 if an error occured. - **/ - inline int fdate(const char *const path, unsigned int attr) { - int out = (int)attr; - return fdate(path,&out,1); - } - - //! Get current local time (multiple-attributes version). - /** - \param[in,out] attr Type of requested time attributes. - Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } - Replaced by read attributes after return (or -1 if an error occured). - \param nb_attr Number of attributes to read/write. - \return Latest read attribute. - **/ - template<typename T> - inline int date(T *attr, const unsigned int nb_attr) { - int res = -1; - cimg::mutex(6); -#if cimg_OS==2 - SYSTEMTIME st; - GetLocalTime(&st); - for (unsigned int i = 0; i<nb_attr; ++i) { - res = (int)(attr[i]==0?st.wYear:attr[i]==1?st.wMonth:attr[i]==2?st.wDay: - attr[i]==3?st.wDayOfWeek:attr[i]==4?st.wHour:attr[i]==5?st.wMinute: - attr[i]==6?st.wSecond:-1); - attr[i] = (T)res; - } -#else - time_t _st; - std::time(&_st); - struct tm *st = std::localtime(&_st); - for (unsigned int i = 0; i<nb_attr; ++i) { - res = (int)(attr[i]==0?st->tm_year + 1900:attr[i]==1?st->tm_mon + 1:attr[i]==2?st->tm_mday: - attr[i]==3?st->tm_wday:attr[i]==4?st->tm_hour:attr[i]==5?st->tm_min: - attr[i]==6?st->tm_sec:-1); - attr[i] = (T)res; - } -#endif - cimg::mutex(6,0); - return res; - } - - //! Get current local time (single-attribute version). - /** - \param attr Type of requested time attribute. - Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } - \return Specified attribute or -1 if an error occured. - **/ - inline int date(unsigned int attr) { - int out = (int)attr; - return date(&out,1); - } - - // Get/set path to store temporary files. - inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the <i>Program Files/</i> directory (Windows only). -#if cimg_OS==2 - inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false); -#endif - - // Get/set path to the ImageMagick's \c convert binary. - inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the GraphicsMagick's \c gm binary. - inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the XMedcon's \c medcon binary. - inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the FFMPEG's \c ffmpeg binary. - inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c gzip binary. - inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c gunzip binary. - inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c dcraw binary. - inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c wget binary. - inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false); - - // Get/set path to the \c curl binary. - inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false); - - //! Split filename into two C-strings \c body and \c extension. - /** - filename and body must not overlap! - **/ - inline const char *split_filename(const char *const filename, char *const body=0) { - if (!filename) { if (body) *body = 0; return 0; } - const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.') + 1) {} - if (p==filename) { - if (body) std::strcpy(body,filename); - return filename + std::strlen(filename); - } - const unsigned int l = (unsigned int)(p - filename - 1); - if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; } - return p; - } - - //! Generate a numbered version of a filename. - inline char* number_filename(const char *const filename, const int number, - const unsigned int digits, char *const str) { - if (!filename) { if (str) *str = 0; return 0; } - char *const format = new char[1024], *const body = new char[1024]; - const char *const ext = cimg::split_filename(filename,body); - if (*ext) cimg_snprintf(format,1024,"%%s_%%.%ud.%%s",digits); - else cimg_snprintf(format,1024,"%%s_%%.%ud",digits); - cimg_sprintf(str,format,body,number,ext); - delete[] format; delete[] body; - return str; - } - - //! Read data from file. - /** - \param[out] ptr Pointer to memory buffer that will contain the binary data read from file. - \param nmemb Number of elements to read. - \param stream File to read data from. - \return Number of read elements. - \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read. - **/ - template<typename T> - inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) { - if (!ptr || !stream) - throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.", - nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr); - if (!nmemb) return 0; - const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); - size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0; - do { - l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit; - l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream); - al_read+=l_al_read; - to_read-=l_al_read; - } while (l_to_read==l_al_read && to_read>0); - if (to_read>0) - warn("cimg::fread(): Only %lu/%lu elements could be read from file.", - (unsigned long)al_read,(unsigned long)nmemb); - return al_read; - } - - //! Write data to file. - /** - \param ptr Pointer to memory buffer containing the binary data to write on file. - \param nmemb Number of elements to write. - \param[out] stream File to write data on. - \return Number of written elements. - \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written. - **/ - template<typename T> - inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) { - if (!ptr || !stream) - throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.", - nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream); - if (!nmemb) return 0; - const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); - size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0; - do { - l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit; - l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream); - al_write+=l_al_write; - to_write-=l_al_write; - } while (l_to_write==l_al_write && to_write>0); - if (to_write>0) - warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.", - (unsigned long)al_write,(unsigned long)nmemb); - return al_write; - } - - //! Create an empty file. - /** - \param file Input file (can be \c 0 if \c filename is set). - \param filename Filename, as a C-string (can be \c 0 if \c file is set). - **/ - inline void fempty(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException("cimg::fempty(): Specified filename is (null)."); - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - if (!file) cimg::fclose(nfile); - } - - // Try to guess format from an image file. - inline const char *ftype(std::FILE *const file, const char *const filename); - - // Load file from network as a local temporary file. - inline char *load_network(const char *const url, char *const filename_local, - const unsigned int timeout=0, const bool try_fallback=false, - const char *const referer=0); - - //! Return options specified on the command line. - inline const char* option(const char *const name, const int argc, const char *const *const argv, - const char *const defaut, const char *const usage, const bool reset_static) { - static bool first = true, visu = false; - if (reset_static) { first = true; return 0; } - const char *res = 0; - if (first) { - first = false; - visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0; - visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0; - visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0; - } - if (!name && visu) { - if (usage) { - std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); - std::fprintf(cimg::output(),": %s",usage); - std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time); - } - if (defaut) std::fprintf(cimg::output(),"%s\n",defaut); - } - if (name) { - if (argc>0) { - int k = 0; - while (k<argc && std::strcmp(argv[k],name)) ++k; - res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k])); - } else res = defaut; - if (visu && usage) std::fprintf(cimg::output()," %s%-16s%s %-24s %s%s%s\n", - cimg::t_bold,name,cimg::t_normal,res?res:"0", - cimg::t_green,usage,cimg::t_normal); - } - return res; - } - - inline const char* option(const char *const name, const int argc, const char *const *const argv, - const char *const defaut, const char *const usage=0) { - return option(name,argc,argv,defaut,usage,false); - } - - inline bool option(const char *const name, const int argc, const char *const *const argv, - const bool defaut, const char *const usage=0) { - const char *const s = cimg::option(name,argc,argv,(char*)0); - const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut; - cimg::option(name,0,0,res?"true":"false",usage); - return res; - } - - inline int option(const char *const name, const int argc, const char *const *const argv, - const int defaut, const char *const usage=0) { - const char *const s = cimg::option(name,argc,argv,(char*)0); - const int res = s?std::atoi(s):defaut; - char *const tmp = new char[256]; - cimg_snprintf(tmp,256,"%d",res); - cimg::option(name,0,0,tmp,usage); - delete[] tmp; - return res; - } - - inline char option(const char *const name, const int argc, const char *const *const argv, - const char defaut, const char *const usage=0) { - const char *const s = cimg::option(name,argc,argv,(char*)0); - const char res = s?*s:defaut; - char tmp[8]; - *tmp = res; tmp[1] = 0; - cimg::option(name,0,0,tmp,usage); - return res; - } - - inline float option(const char *const name, const int argc, const char *const *const argv, - const float defaut, const char *const usage=0) { - const char *const s = cimg::option(name,argc,argv,(char*)0); - const float res = s?(float)cimg::atof(s):defaut; - char *const tmp = new char[256]; - cimg_snprintf(tmp,256,"%g",res); - cimg::option(name,0,0,tmp,usage); - delete[] tmp; - return res; - } - - inline double option(const char *const name, const int argc, const char *const *const argv, - const double defaut, const char *const usage=0) { - const char *const s = cimg::option(name,argc,argv,(char*)0); - const double res = s?cimg::atof(s):defaut; - char *const tmp = new char[256]; - cimg_snprintf(tmp,256,"%g",res); - cimg::option(name,0,0,tmp,usage); - delete[] tmp; - return res; - } - - //! Print information about \CImg environement variables. - /** - \note Output is done on the default output stream. - **/ - inline void info() { - std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n", - cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10, - cimg::t_normal,cimg_date,cimg_time); - - std::fprintf(cimg::output()," > Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n", - cimg::t_bold, - cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"), - cimg::t_normal,cimg::t_green, - cimg_OS, - cimg::t_normal); - - std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n", - cimg::t_bold, - cimg::endianness()?"Big":"Little", - cimg::t_normal); - - std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n", - cimg::t_bold, - cimg_verbosity==0?"Quiet": - cimg_verbosity==1?"Console": - cimg_verbosity==2?"Dialog": - cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings", - cimg::t_normal,cimg::t_green, - cimg_verbosity, - cimg::t_normal); - - std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n", - cimg::t_bold, -#ifdef cimg_strict_warnings - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - std::fprintf(cimg::output()," > Support for C++11: %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n", - cimg::t_bold, - cimg_use_cpp11?"Yes":"No", - cimg::t_normal,cimg::t_green, - (int)cimg_use_cpp11, - cimg::t_normal); - - std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_vt100 - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n", - cimg::t_bold, - cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown", - cimg::t_normal,cimg::t_green, - (int)cimg_display, - cimg::t_normal); - -#if cimg_display==1 - std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_xshm - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_xrandr - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); -#endif - std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_openmp - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_png - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_jpeg - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_tiff - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_magick - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_fftw3 - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n", - cimg::t_bold, -#ifdef cimg_use_lapack - "Yes",cimg::t_normal,cimg::t_green,"defined", -#else - "No",cimg::t_normal,cimg::t_green,"undefined", -#endif - cimg::t_normal); - - char *const tmp = new char[1024]; - cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path()); - std::fprintf(cimg::output()," > Path of ImageMagick: %s%-13s%s\n", - cimg::t_bold, - tmp, - cimg::t_normal); - - cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path()); - std::fprintf(cimg::output()," > Path of GraphicsMagick: %s%-13s%s\n", - cimg::t_bold, - tmp, - cimg::t_normal); - - cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path()); - std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n", - cimg::t_bold, - tmp, - cimg::t_normal); - - cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path()); - std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n", - cimg::t_bold, - tmp, - cimg::t_normal); - - std::fprintf(cimg::output(),"\n"); - delete[] tmp; - } - - // Declare LAPACK function signatures if LAPACK support is enabled. -#ifdef cimg_use_lapack - template<typename T> - inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { - dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); - } - - inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) { - sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); - } - - template<typename T> - inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { - dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); - } - - inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { - sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); - } - - template<typename T> - inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN, - T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) { - dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); - } - - inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN, - float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) { - sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); - } - - template<typename T> - inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) { - int one = 1; - dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); - } - - inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) { - int one = 1; - sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); - } - - template<typename T> - inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { - dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); - } - - inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { - ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); - } - - template<typename T> - inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA, - T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){ - dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); - } - - inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA, - float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){ - sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); - } - -#endif - - // End of the 'cimg' namespace - } - - /*------------------------------------------------ - # - # - # Definition of mathematical operators and - # external functions. - # - # - -------------------------------------------------*/ - -#define _cimg_create_ext_operators(typ) \ - template<typename T> \ - inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \ - return img + val; \ - } \ - template<typename T> \ - inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \ - typedef typename cimg::superset<T,typ>::type Tt; \ - return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \ - } \ - template<typename T> \ - inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \ - return img*val; \ - } \ - template<typename T> \ - inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \ - return val*img.get_invert(); \ - } \ - template<typename T> \ - inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \ - return img & val; \ - } \ - template<typename T> \ - inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \ - return img | val; \ - } \ - template<typename T> \ - inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \ - return img ^ val; \ - } \ - template<typename T> \ - inline bool operator==(const typ val, const CImg<T>& img) { \ - return img == val; \ - } \ - template<typename T> \ - inline bool operator!=(const typ val, const CImg<T>& img) { \ - return img != val; \ - } - - _cimg_create_ext_operators(bool) - _cimg_create_ext_operators(unsigned char) - _cimg_create_ext_operators(char) - _cimg_create_ext_operators(signed char) - _cimg_create_ext_operators(unsigned short) - _cimg_create_ext_operators(short) - _cimg_create_ext_operators(unsigned int) - _cimg_create_ext_operators(int) - _cimg_create_ext_operators(cimg_uint64) - _cimg_create_ext_operators(cimg_int64) - _cimg_create_ext_operators(float) - _cimg_create_ext_operators(double) - _cimg_create_ext_operators(long double) - - template<typename T> - inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) { - return img + expression; - } - - template<typename T> - inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) { - return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img; - } - - template<typename T> - inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) { - return img*expression; - } - - template<typename T> - inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) { - return expression*img.get_invert(); - } - - template<typename T> - inline CImg<T> operator&(const char *const expression, const CImg<T>& img) { - return img & expression; - } - - template<typename T> - inline CImg<T> operator|(const char *const expression, const CImg<T>& img) { - return img | expression; - } - - template<typename T> - inline CImg<T> operator^(const char *const expression, const CImg<T>& img) { - return img ^ expression; - } - - template<typename T> - inline bool operator==(const char *const expression, const CImg<T>& img) { - return img==expression; - } - - template<typename T> - inline bool operator!=(const char *const expression, const CImg<T>& img) { - return img!=expression; - } - - template<typename T> - inline CImg<T> transpose(const CImg<T>& instance) { - return instance.get_transpose(); - } - - template<typename T> - inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) { - return instance.get_invert(); - } - - template<typename T> - inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) { - return instance.get_pseudoinvert(); - } - -#define _cimg_create_ext_pointwise_function(name) \ - template<typename T> \ - inline CImg<_cimg_Tfloat> name(const CImg<T>& instance) { \ - return instance.get_##name(); \ - } - - _cimg_create_ext_pointwise_function(sqr) - _cimg_create_ext_pointwise_function(sqrt) - _cimg_create_ext_pointwise_function(exp) - _cimg_create_ext_pointwise_function(log) - _cimg_create_ext_pointwise_function(log2) - _cimg_create_ext_pointwise_function(log10) - _cimg_create_ext_pointwise_function(abs) - _cimg_create_ext_pointwise_function(sign) - _cimg_create_ext_pointwise_function(cos) - _cimg_create_ext_pointwise_function(sin) - _cimg_create_ext_pointwise_function(sinc) - _cimg_create_ext_pointwise_function(tan) - _cimg_create_ext_pointwise_function(acos) - _cimg_create_ext_pointwise_function(asin) - _cimg_create_ext_pointwise_function(atan) - _cimg_create_ext_pointwise_function(cosh) - _cimg_create_ext_pointwise_function(sinh) - _cimg_create_ext_pointwise_function(tanh) - _cimg_create_ext_pointwise_function(acosh) - _cimg_create_ext_pointwise_function(asinh) - _cimg_create_ext_pointwise_function(atanh) - - /*----------------------------------- - # - # Define the CImgDisplay structure - # - ----------------------------------*/ - //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events). - /** - CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window - (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems). - If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter - a minimal mode where warning messages will be outputed each time the program is trying to call one of the - CImgDisplay method. - - The configuration variable \c cimg_display tells about the graphic library used. - It is set automatically by \CImg when one of these graphic libraries has been detected. - But, you can override its value if necessary. Valid choices are: - - 0: Disable display capabilities. - - 1: Use \b X-Window (X11) library. - - 2: Use \b GDI32 library. - - Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay. - **/ - struct CImgDisplay { - cimg_ulong _timer, _fps_frames, _fps_timer; - unsigned int _width, _height, _normalization; - float _fps_fps, _min, _max; - bool _is_fullscreen; - char *_title; - unsigned int _window_width, _window_height, _button, *_keys, *_released_keys; - int _window_x, _window_y, _mouse_x, _mouse_y, _wheel; - bool _is_closed, _is_resized, _is_moved, _is_event, - _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7, - _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2, - _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0, - _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE, - _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE, - _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG, - _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX, - _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT, - _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT, - _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3, - _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB, - _is_keyPADMUL, _is_keyPADDIV; - - //@} - //--------------------------- - // - //! \name Plugins - //@{ - //--------------------------- - -#ifdef cimgdisplay_plugin -#include cimgdisplay_plugin -#endif -#ifdef cimgdisplay_plugin1 -#include cimgdisplay_plugin1 -#endif -#ifdef cimgdisplay_plugin2 -#include cimgdisplay_plugin2 -#endif -#ifdef cimgdisplay_plugin3 -#include cimgdisplay_plugin3 -#endif -#ifdef cimgdisplay_plugin4 -#include cimgdisplay_plugin4 -#endif -#ifdef cimgdisplay_plugin5 -#include cimgdisplay_plugin5 -#endif -#ifdef cimgdisplay_plugin6 -#include cimgdisplay_plugin6 -#endif -#ifdef cimgdisplay_plugin7 -#include cimgdisplay_plugin7 -#endif -#ifdef cimgdisplay_plugin8 -#include cimgdisplay_plugin8 -#endif - - //@} - //-------------------------------------------------------- - // - //! \name Constructors / Destructor / Instance Management - //@{ - //-------------------------------------------------------- - - //! Destructor. - /** - \note If the associated window is visible on the screen, it is closed by the call to the destructor. - **/ - ~CImgDisplay() { - assign(); - delete[] _keys; - delete[] _released_keys; - } - - //! Construct an empty display. - /** - \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until - display of valid data is performed. - \par Example - \code - CImgDisplay disp; // Does actually nothing - ... - disp.display(img); // Construct new window and display image in it - \endcode - **/ - CImgDisplay(): - _width(0),_height(0),_normalization(0), - _min(0),_max(0), - _is_fullscreen(false), - _title(0), - _window_width(0),_window_height(0),_button(0), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), - _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { - assign(); - } - - //! Construct a display with specified dimensions. - /** \param width Window width. - \param height Window height. - \param title Window title. - \param normalization Normalization type - (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()). - \param is_fullscreen Tells if fullscreen mode is enabled. - \param is_closed Tells if associated window is initially visible or not. - \note A black background is initially displayed on the associated window. - **/ - CImgDisplay(const unsigned int width, const unsigned int height, - const char *const title=0, const unsigned int normalization=3, - const bool is_fullscreen=false, const bool is_closed=false): - _width(0),_height(0),_normalization(0), - _min(0),_max(0), - _is_fullscreen(false), - _title(0), - _window_width(0),_window_height(0),_button(0), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), - _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { - assign(width,height,title,normalization,is_fullscreen,is_closed); - } - - //! Construct a display from an image. - /** \param img Image used as a model to create the window. - \param title Window title. - \param normalization Normalization type - (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()). - \param is_fullscreen Tells if fullscreen mode is enabled. - \param is_closed Tells if associated window is initially visible or not. - \note The pixels of the input image are initially displayed on the associated window. - **/ - template<typename T> - explicit CImgDisplay(const CImg<T>& img, - const char *const title=0, const unsigned int normalization=3, - const bool is_fullscreen=false, const bool is_closed=false): - _width(0),_height(0),_normalization(0), - _min(0),_max(0), - _is_fullscreen(false), - _title(0), - _window_width(0),_window_height(0),_button(0), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), - _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { - assign(img,title,normalization,is_fullscreen,is_closed); - } - - //! Construct a display from an image list. - /** \param list The images list to display. - \param title Window title. - \param normalization Normalization type - (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()). - \param is_fullscreen Tells if fullscreen mode is enabled. - \param is_closed Tells if associated window is initially visible or not. - \note All images of the list, appended along the X-axis, are initially displayed on the associated window. - **/ - template<typename T> - explicit CImgDisplay(const CImgList<T>& list, - const char *const title=0, const unsigned int normalization=3, - const bool is_fullscreen=false, const bool is_closed=false): - _width(0),_height(0),_normalization(0), - _min(0),_max(0), - _is_fullscreen(false), - _title(0), - _window_width(0),_window_height(0),_button(0), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), - _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { - assign(list,title,normalization,is_fullscreen,is_closed); - } - - //! Construct a display as a copy of an existing one. - /** - \param disp Display instance to copy. - \note The pixel buffer of the input window is initially displayed on the associated window. - **/ - CImgDisplay(const CImgDisplay& disp): - _width(0),_height(0),_normalization(0), - _min(0),_max(0), - _is_fullscreen(false), - _title(0), - _window_width(0),_window_height(0),_button(0), - _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), - _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0), - _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { - assign(disp); - } - - //! Take a screenshot. - /** - \param[out] img Output screenshot. Can be empty on input - **/ - template<typename T> - static void screenshot(CImg<T>& img) { - return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img); - } - -#if cimg_display==0 - - static void _no_display_exception() { - throw CImgDisplayException("CImgDisplay(): No display available."); - } - - //! Destructor - Empty constructor \inplace. - /** - \note Replace the current instance by an empty display. - **/ - CImgDisplay& assign() { - return flush(); - } - - //! Construct a display with specified dimensions \inplace. - /** - **/ - CImgDisplay& assign(const unsigned int width, const unsigned int height, - const char *const title=0, const unsigned int normalization=3, - const bool is_fullscreen=false, const bool is_closed=false) { - cimg::unused(width,height,title,normalization,is_fullscreen,is_closed); - _no_display_exception(); - return assign(); - } - - //! Construct a display from an image \inplace. - /** - **/ - template<typename T> - CImgDisplay& assign(const CImg<T>& img, - const char *const title=0, const unsigned int normalization=3, - const bool is_fullscreen=false, const bool is_closed=false) { - _no_display_exception(); - return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed); - } - - //! Construct a display from an image list \inplace. - /** - **/ - template<typename T> - CImgDisplay& assign(const CImgList<T>& list, - const char *const title=0, const unsigned int normalization=3, - const bool is_fullscreen=false, const bool is_closed=false) { - _no_display_exception(); - return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed); - } - - //! Construct a display as a copy of another one \inplace. - /** - **/ - CImgDisplay& assign(const CImgDisplay &disp) { - _no_display_exception(); - return assign(disp._width,disp._height); - } - -#endif - - //! Return a reference to an empty display. - /** - \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&) - must have a default value. - \par Example - \code - void foo(CImgDisplay& disp=CImgDisplay::empty()); - \endcode - **/ - static CImgDisplay& empty() { - static CImgDisplay _empty; - return _empty.assign(); - } - - //! Return a reference to an empty display \const. - static const CImgDisplay& const_empty() { - static const CImgDisplay _empty; - return _empty; - } - -#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,256,-85,false), \ - CImgDisplay::_fitscreen(dx,dy,dz,256,-85,true) - static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz, - const int dmin, const int dmax, const bool return_y) { - const int - u = CImgDisplay::screen_width(), - v = CImgDisplay::screen_height(); - const float - mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin, - mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin, - Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax, - Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax; - float - w = (float)std::max(1U,dx), - h = (float)std::max(1U,dy); - if (dz>1) { w+=dz; h+=dz; } - if (w<mw) { h = h*mw/w; w = mw; } - if (h<mh) { w = w*mh/h; h = mh; } - if (w>Mw) { h = h*Mw/w; w = Mw; } - if (h>Mh) { w = w*Mh/h; h = Mh; } - if (w<mw) w = mw; - if (h<mh) h = mh; - return std::max(1U,(unsigned int)cimg::round(return_y?h:w)); - } - - //@} - //------------------------------------------ - // - //! \name Overloaded Operators - //@{ - //------------------------------------------ - - //! Display image on associated window. - /** - \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>. - **/ - template<typename t> - CImgDisplay& operator=(const CImg<t>& img) { - return display(img); - } - - //! Display list of images on associated window. - /** - \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>. - **/ - template<typename t> - CImgDisplay& operator=(const CImgList<t>& list) { - return display(list); - } - - //! Construct a display as a copy of another one \inplace. - /** - \note Equivalent to assign(const CImgDisplay&). - **/ - CImgDisplay& operator=(const CImgDisplay& disp) { - return assign(disp); - } - - //! Return \c false if display is empty, \c true otherwise. - /** - \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>. - **/ - operator bool() const { - return !is_empty(); - } - - //@} - //------------------------------------------ - // - //! \name Instance Checking - //@{ - //------------------------------------------ - - //! Return \c true if display is empty, \c false otherwise. - /** - **/ - bool is_empty() const { - return !(_width && _height); - } - - //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise. - /** - \note - - When a user physically closes the associated window, the display is set to closed. - - A closed display is not destroyed. Its associated window can be show again on the screen using show(). - **/ - bool is_closed() const { - return _is_closed; - } - - //! Return \c true if associated window has been resized on the screen, \c false otherwise. - /** - **/ - bool is_resized() const { - return _is_resized; - } - - //! Return \c true if associated window has been moved on the screen, \c false otherwise. - /** - **/ - bool is_moved() const { - return _is_moved; - } - - //! Return \c true if any event has occured on the associated window, \c false otherwise. - /** - **/ - bool is_event() const { - return _is_event; - } - - //! Return \c true if current display is in fullscreen mode, \c false otherwise. - /** - **/ - bool is_fullscreen() const { - return _is_fullscreen; - } - - //! Return \c true if any key is being pressed on the associated window, \c false otherwise. - /** - \note The methods below do the same only for specific keys. - **/ - bool is_key() const { - return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 || - _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 || - _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 || - _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 || - _is_key3 || _is_key4 || _is_key5 || _is_key6 || - _is_key7 || _is_key8 || _is_key9 || _is_key0 || - _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME || - _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW || - _is_keyE || _is_keyR || _is_keyT || _is_keyY || - _is_keyU || _is_keyI || _is_keyO || _is_keyP || - _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN || - _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD || - _is_keyF || _is_keyG || _is_keyH || _is_keyJ || - _is_keyK || _is_keyL || _is_keyENTER || - _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC || - _is_keyV || _is_keyB || _is_keyN || _is_keyM || - _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT || - _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR || - _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT || - _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT || - _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 || - _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 || - _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 || - _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB || - _is_keyPADMUL || _is_keyPADDIV; - } - - //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. - /** - \param keycode Keycode to test. - \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure - your code stay portable (see cimg::keyESC). - \par Example - \code - CImgDisplay disp(400,400); - while (!disp.is_closed()) { - if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())' - disp.wait(); - } - \endcode - **/ - bool is_key(const unsigned int keycode) const { -#define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k; - _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3); - _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7); - _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11); - _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2); - _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6); - _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0); - _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME); - _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W); - _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y); - _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P); - _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN); - _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D); - _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J); - _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER); - _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C); - _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M); - _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT); - _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR); - _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT); - _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT); - _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2); - _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5); - _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8); - _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB); - _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV); - return false; - } - - //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. - /** - \param keycode C-string containing the keycode label of the key to test. - \note Use it when the key you want to test can be dynamically set by the user. - \par Example - \code - CImgDisplay disp(400,400); - const char *const keycode = "TAB"; - while (!disp.is_closed()) { - if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())' - disp.wait(); - } - \endcode - **/ - bool& is_key(const char *const keycode) { - static bool f = false; - f = false; -#define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k; - _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3); - _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7); - _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11); - _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2); - _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6); - _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0); - _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME); - _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W); - _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y); - _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P); - _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN); - _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D); - _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J); - _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER); - _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C); - _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M); - _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT); - _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR); - _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT); - _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT); - _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2); - _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5); - _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8); - _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB); - _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV); - return f; - } - - //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise. - /** - \param keycodes_sequence Buffer of keycodes to test. - \param length Number of keys in the \c keycodes_sequence buffer. - \param remove_sequence Tells if the key sequence must be removed from the key history, if found. - \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure - your code stay portable (see cimg::keyESC). - \par Example - \code - CImgDisplay disp(400,400); - const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD }; - while (!disp.is_closed()) { - if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event - disp.wait(); - } - \endcode - **/ - bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, - const bool remove_sequence=false) { - if (keycodes_sequence && length) { - const unsigned int - *const ps_end = keycodes_sequence + length - 1, - *const pk_end = (unsigned int*)_keys + 1 + 128 - length, - k = *ps_end; - for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) { - if (*(pk++)==k) { - bool res = true; - const unsigned int *ps = ps_end, *pk2 = pk; - for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++)); - if (res) { - if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length); - return true; - } - } - } - } - return false; - } - -#define _cimg_iskey_def(k) \ - bool is_key##k() const { \ - return _is_key##k; \ - } - - //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise. - /** - \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC). - **/ - _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3); - _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7); - _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11); - _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2); - _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6); - _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0); - _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME); - _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W); - _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y); - _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P); - _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN); - _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D); - _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J); - _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER); - _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C); - _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M); - _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT); - _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR); - _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT); - _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT); - _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2); - _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5); - _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8); - _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB); - _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV); - - //@} - //------------------------------------------ - // - //! \name Instance Characteristics - //@{ - //------------------------------------------ - -#if cimg_display==0 - - //! Return width of the screen (current resolution along the X-axis). - /** - **/ - static int screen_width() { - _no_display_exception(); - return 0; - } - - //! Return height of the screen (current resolution along the Y-axis). - /** - **/ - static int screen_height() { - _no_display_exception(); - return 0; - } - -#endif - - //! Return display width. - /** - \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance) - may be different from the actual width of the associated window. - **/ - int width() const { - return (int)_width; - } - - //! Return display height. - /** - \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance) - may be different from the actual height of the associated window. - **/ - int height() const { - return (int)_height; - } - - //! Return normalization type of the display. - /** - The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be - correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>. - If the range of values of the data to display is different, a normalization may be required for displaying - the data in a correct way. The normalization type can be one of: - - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the - CImgDisplay instance have values in range <tt>[0,255]</tt>. - - \c 1: Value normalization is always performed (this is the default behavior). - Before displaying an input image, its values will be (virtually) stretched - in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum. - Use this mode for images whose minimum and maximum values are not prescribed to known values - (e.g. float-valued images). - Note that when normalized versions of images are computed for display purposes, the actual values of these - images are not modified. - - \c 2: Value normalization is performed once (on the first image display), then the same normalization - coefficients are kept for next displayed frames. - - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types, - the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then - for <tt>unsigned char</tt>). - For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image - data instead. - **/ - unsigned int normalization() const { - return _normalization; - } - - //! Return title of the associated window as a C-string. - /** - \note Window title may be not visible, depending on the used window manager or if the current display is - in fullscreen mode. - **/ - const char *title() const { - return _title?_title:""; - } - - //! Return width of the associated window. - /** - \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance) - may be different from the actual width of the associated window. - **/ - int window_width() const { - return (int)_window_width; - } - - //! Return height of the associated window. - /** - \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance) - may be different from the actual height of the associated window. - **/ - int window_height() const { - return (int)_window_height; - } - - //! Return X-coordinate of the associated window. - /** - \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. - **/ - int window_x() const { - return _window_x; - } - - //! Return Y-coordinate of the associated window. - /** - \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. - **/ - int window_y() const { - return _window_y; - } - - //! Return X-coordinate of the mouse pointer. - /** - \note - - If the mouse pointer is outside window area, \c -1 is returned. - - Otherwise, the returned value is in the range [0,width()-1]. - **/ - int mouse_x() const { - return _mouse_x; - } - - //! Return Y-coordinate of the mouse pointer. - /** - \note - - If the mouse pointer is outside window area, \c -1 is returned. - - Otherwise, the returned value is in the range [0,height()-1]. - **/ - int mouse_y() const { - return _mouse_y; - } - - //! Return current state of the mouse buttons. - /** - \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned - value is set: - - bit \c 0 (value \c 0x1): State of the left mouse button. - - bit \c 1 (value \c 0x2): State of the right mouse button. - - bit \c 2 (value \c 0x4): State of the middle mouse button. - - Several bits can be activated if more than one button are pressed at the same time. - \par Example - \code - CImgDisplay disp(400,400); - while (!disp.is_closed()) { - if (disp.button()&1) { // Left button clicked - ... - } - if (disp.button()&2) { // Right button clicked - ... - } - if (disp.button()&4) { // Middle button clicked - ... - } - disp.wait(); - } - \endcode - **/ - unsigned int button() const { - return _button; - } - - //! Return current state of the mouse wheel. - /** - \note - - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled - forward or backward. - - Scrolling the wheel forward add \c 1 to the wheel value. - - Scrolling the wheel backward substract \c 1 to the wheel value. - - The returned value cumulates the number of forward of backward scrolls since the creation of the display, - or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset - the wheel counter when an action has been performed regarding the current wheel value. - Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done - (as many in forward as in backward directions). - \par Example - \code - CImgDisplay disp(400,400); - while (!disp.is_closed()) { - if (disp.wheel()) { - int counter = disp.wheel(); // Read the state of the mouse wheel - ... // Do what you want with 'counter' - disp.set_wheel(); // Reset the wheel value to 0 - } - disp.wait(); - } - \endcode - **/ - int wheel() const { - return _wheel; - } - - //! Return one entry from the pressed keys history. - /** - \param pos Indice to read from the pressed keys history (indice \c 0 corresponds to latest entry). - \return Keycode of a pressed key or \c 0 for a released key. - \note - - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed, - its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead. - This means that up to the 64 last pressed keys may be read from the pressed keys history. - When a new value is stored, the pressed keys history is shifted so that the latest entry is always - stored at position \c 0. - - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure - your code stay portable (see cimg::keyESC). - **/ - unsigned int key(const unsigned int pos=0) const { - return pos<128?_keys[pos]:0; - } - - //! Return one entry from the released keys history. - /** - \param pos Indice to read from the released keys history (indice \c 0 corresponds to latest entry). - \return Keycode of a released key or \c 0 for a pressed key. - \note - - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released, - its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead. - This means that up to the 64 last released keys may be read from the released keys history. - When a new value is stored, the released keys history is shifted so that the latest entry is always - stored at position \c 0. - - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure - your code stay portable (see cimg::keyESC). - **/ - unsigned int released_key(const unsigned int pos=0) const { - return pos<128?_released_keys[pos]:0; - } - - //! Return keycode corresponding to the specified string. - /** - \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure - your code stay portable (see cimg::keyESC). - \par Example - \code - const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB - \endcode - **/ - static unsigned int keycode(const char *const keycode) { -#define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k; - _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3); - _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7); - _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11); - _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2); - _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6); - _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0); - _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME); - _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W); - _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y); - _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P); - _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN); - _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D); - _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J); - _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER); - _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C); - _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M); - _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT); - _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR); - _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT); - _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT); - _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2); - _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5); - _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8); - _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB); - _cimg_keycode(PADMUL); _cimg_keycode(PADDIV); - return 0; - } - - //! Return the current refresh rate, in frames per second. - /** - \note Returns a significant value when the current instance is used to display successive frames. - It measures the delay between successive calls to frames_per_second(). - **/ - float frames_per_second() { - if (!_fps_timer) _fps_timer = cimg::time(); - const float delta = (cimg::time() - _fps_timer)/1000.f; - ++_fps_frames; - if (delta>=1) { - _fps_fps = _fps_frames/delta; - _fps_frames = 0; - _fps_timer = cimg::time(); - } - return _fps_fps; - } - - //@} - //--------------------------------------- - // - //! \name Window Manipulation - //@{ - //--------------------------------------- - -#if cimg_display==0 - - //! Display image on associated window. - /** - \param img Input image to display. - \note This method returns immediately. - **/ - template<typename T> - CImgDisplay& display(const CImg<T>& img) { - return assign(img); - } - -#endif - - //! Display list of images on associated window. - /** - \param list List of images to display. - \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c). - \param align Relative position of aligned images when displaying lists with images of different sizes - (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right). - \note This method returns immediately. - **/ - template<typename T> - CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) { - if (list._width==1) { - const CImg<T>& img = list[0]; - if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img); - } - CImgList<typename CImg<T>::ucharT> visu(list._width); - unsigned int dims = 0; - cimglist_for(list,l) { - const CImg<T>& img = list._data[l]; - img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2, - (img._depth - 1)/2).move_to(visu[l]); - dims = std::max(dims,visu[l]._spectrum); - } - cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1); - visu.get_append(axis,align).display(*this); - return *this; - } - -#if cimg_display==0 - - //! Show (closed) associated window on the screen. - /** - \note - - Force the associated window of a display to be visible on the screen, even if it has been closed before. - - Using show() on a visible display does nothing. - **/ - CImgDisplay& show() { - return assign(); - } - - //! Close (visible) associated window and make it disappear from the screen. - /** - \note - - A closed display only means the associated window is not visible anymore. This does not mean the display has - been destroyed. - Use show() to make the associated window reappear. - - Using close() on a closed display does nothing. - **/ - CImgDisplay& close() { - return assign(); - } - - //! Move associated window to a new location. - /** - \param pos_x X-coordinate of the new window location. - \param pos_y Y-coordinate of the new window location. - \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown - nevertheless). - **/ - CImgDisplay& move(const int pos_x, const int pos_y) { - return assign(pos_x,pos_y); - } - -#endif - - //! Resize display to the size of the associated window. - /** - \param force_redraw Tells if the previous window content must be updated and refreshed as well. - \note - - Calling this method ensures that width() and window_width() become equal, as well as height() and - window_height(). - - The associated window is also resized to specified dimensions. - **/ - CImgDisplay& resize(const bool force_redraw=true) { - resize(window_width(),window_height(),force_redraw); - return *this; - } - -#if cimg_display==0 - - //! Resize display to the specified size. - /** - \param width Requested display width. - \param height Requested display height. - \param force_redraw Tells if the previous window content must be updated and refreshed as well. - \note The associated window is also resized to specified dimensions. - **/ - CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) { - return assign(width,height,0,3,force_redraw); - } - -#endif - - //! Resize display to the size of an input image. - /** - \param img Input image to take size from. - \param force_redraw Tells if the previous window content must be resized and updated as well. - \note - - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and - <tt>img.height()</tt>. - - The associated window is also resized to specified dimensions. - **/ - template<typename T> - CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) { - return resize(img._width,img._height,force_redraw); - } - - //! Resize display to the size of another CImgDisplay instance. - /** - \param disp Input display to take size from. - \param force_redraw Tells if the previous window content must be resized and updated as well. - \note - - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and - <tt>disp.height()</tt>. - - The associated window is also resized to specified dimensions. - **/ - CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) { - return resize(disp.width(),disp.height(),force_redraw); - } - - // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs). - template<typename t, typename T> - static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, - t *ptrd, const unsigned int wd, const unsigned int hd) { - typedef typename cimg::last<T,cimg_ulong>::type ulongT; - const ulongT one = (ulongT)1; - CImg<ulongT> off_x(wd), off_y(hd + 1); - if (wd==ws) off_x.fill(1); - else { - ulongT *poff_x = off_x._data, curr = 0; - for (unsigned int x = 0; x<wd; ++x) { - const ulongT old = curr; - curr = (x + one)*ws/wd; - *(poff_x++) = curr - old; - } - } - if (hd==hs) off_y.fill(ws); - else { - ulongT *poff_y = off_y._data, curr = 0; - for (unsigned int y = 0; y<hd; ++y) { - const ulongT old = curr; - curr = (y + one)*hs/hd; - *(poff_y++) = ws*(curr - old); - } - *poff_y = 0; - } - ulongT *poff_y = off_y._data; - for (unsigned int y = 0; y<hd; ) { - const T *ptr = ptrs; - ulongT *poff_x = off_x._data; - for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poff_x++); } - ++y; - ulongT dy = *(poff_y++); - for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poff_y++)) {} - ptrs+=dy; - } - } - - //! Set normalization type. - /** - \param normalization New normalization mode. - **/ - CImgDisplay& set_normalization(const unsigned int normalization) { - _normalization = normalization; - _min = _max = 0; - return *this; - } - -#if cimg_display==0 - - //! Set title of the associated window. - /** - \param format C-string containing the format of the title, as with <tt>std::printf()</tt>. - \warning As the first argument is a format string, it is highly recommended to write - \code - disp.set_title("%s",window_title); - \endcode - instead of - \code - disp.set_title(window_title); - \endcode - if \c window_title can be arbitrary, to prevent nasty memory access. - **/ - CImgDisplay& set_title(const char *const format, ...) { - return assign(0,0,format); - } - -#endif - - //! Enable or disable fullscreen mode. - /** - \param is_fullscreen Tells is the fullscreen mode must be activated or not. - \param force_redraw Tells if the previous window content must be displayed as well. - \note - - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the - current display is not modified. - - The screen resolution may be switched to fit the associated window size and ensure it appears the largest - as possible. - For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen - resolution change (requires the X11 extensions to be enabled). - **/ - CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) { - if (is_empty() || _is_fullscreen==is_fullscreen) return *this; - return toggle_fullscreen(force_redraw); - } - -#if cimg_display==0 - - //! Toggle fullscreen mode. - /** - \param force_redraw Tells if the previous window content must be displayed as well. - \note Enable fullscreen mode if it was not enabled, and disable it otherwise. - **/ - CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { - return assign(_width,_height,0,3,force_redraw); - } - - //! Show mouse pointer. - /** - \note Depending on the window manager behavior, this method may not succeed - (no exceptions are thrown nevertheless). - **/ - CImgDisplay& show_mouse() { - return assign(); - } - - //! Hide mouse pointer. - /** - \note Depending on the window manager behavior, this method may not succeed - (no exceptions are thrown nevertheless). - **/ - CImgDisplay& hide_mouse() { - return assign(); - } - - //! Move mouse pointer to a specified location. - /** - \note Depending on the window manager behavior, this method may not succeed - (no exceptions are thrown nevertheless). - **/ - CImgDisplay& set_mouse(const int pos_x, const int pos_y) { - return assign(pos_x,pos_y); - } - -#endif - - //! Simulate a mouse button release event. - /** - \note All mouse buttons are considered released at the same time. - **/ - CImgDisplay& set_button() { - _button = 0; - _is_event = true; -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - return *this; - } - - //! Simulate a mouse button press or release event. - /** - \param button Buttons event code, where each button is associated to a single bit. - \param is_pressed Tells if the mouse button is considered as pressed or released. - **/ - CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) { - const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U; - if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode; - _is_event = buttoncode?true:false; - if (buttoncode) { -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - } - return *this; - } - - //! Flush all mouse wheel events. - /** - \note Make wheel() to return \c 0, if called afterwards. - **/ - CImgDisplay& set_wheel() { - _wheel = 0; - _is_event = true; -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - return *this; - } - - //! Simulate a wheel event. - /** - \param amplitude Amplitude of the wheel scrolling to simulate. - \note Make wheel() to return \c amplitude, if called afterwards. - **/ - CImgDisplay& set_wheel(const int amplitude) { - _wheel+=amplitude; - _is_event = amplitude?true:false; - if (amplitude) { -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - } - return *this; - } - - //! Flush all key events. - /** - \note Make key() to return \c 0, if called afterwards. - **/ - CImgDisplay& set_key() { - std::memset((void*)_keys,0,128*sizeof(unsigned int)); - std::memset((void*)_released_keys,0,128*sizeof(unsigned int)); - _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = - _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = - _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = - _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = - _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = - _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL = - _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN = - _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = - _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = - _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = - _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL = - _is_keyPADDIV = false; - _is_event = true; -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - return *this; - } - - //! Simulate a keyboard press/release event. - /** - \param keycode Keycode of the associated key. - \param is_pressed Tells if the key is considered as pressed or released. - \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure - your code stay portable (see cimg::keyESC). - **/ - CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) { -#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed; - _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3); - _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7); - _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11); - _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2); - _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6); - _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0); - _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME); - _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W); - _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y); - _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P); - _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN); - _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D); - _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J); - _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER); - _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C); - _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M); - _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT); - _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR); - _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT); - _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT); - _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2); - _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5); - _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8); - _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB); - _cimg_set_key(PADMUL); _cimg_set_key(PADDIV); - if (is_pressed) { - if (*_keys) - std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); - *_keys = keycode; - if (*_released_keys) { - std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); - *_released_keys = 0; - } - } else { - if (*_keys) { - std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); - *_keys = 0; - } - if (*_released_keys) - std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); - *_released_keys = keycode; - } - _is_event = keycode?true:false; - if (keycode) { -#if cimg_display==1 - pthread_cond_broadcast(&cimg::X11_attr().wait_event); -#elif cimg_display==2 - SetEvent(cimg::Win32_attr().wait_event); -#endif - } - return *this; - } - - //! Flush all display events. - /** - \note Remove all passed events from the current display. - **/ - CImgDisplay& flush() { - set_key().set_button().set_wheel(); - _is_resized = _is_moved = _is_event = false; - _fps_timer = _fps_frames = _timer = 0; - _fps_fps = 0; - return *this; - } - - //! Wait for any user event occuring on the current display. - CImgDisplay& wait() { - wait(*this); - return *this; - } - - //! Wait for a given number of milliseconds since the last call to wait(). - /** - \param milliseconds Number of milliseconds to wait for. - \note Similar to cimg::wait(). - **/ - CImgDisplay& wait(const unsigned int milliseconds) { - cimg::wait(milliseconds,&_timer); - return *this; - } - - //! Wait for any event occuring on the display \c disp1. - static void wait(CImgDisplay& disp1) { - disp1._is_event = false; - while (!disp1._is_closed && !disp1._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1 or \c disp2. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { - disp1._is_event = disp2._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed) && - !disp1._is_event && !disp2._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { - disp1._is_event = disp2._is_event = disp3._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { - disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, - CImgDisplay& disp5) { - disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) - wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, - CImgDisplay& disp6) { - disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = - disp6._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || - !disp6._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && - !disp6._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, - CImgDisplay& disp6, CImgDisplay& disp7) { - disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = - disp6._is_event = disp7._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || - !disp6._is_closed || !disp7._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && - !disp6._is_event && !disp7._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, - CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) { - disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = - disp6._is_event = disp7._is_event = disp8._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || - !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && - !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, - CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) { - disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = - disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || - !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && - !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all(); - } - - //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10. - static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, - CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, - CImgDisplay& disp10) { - disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = - disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false; - while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || - !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) && - !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && - !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) - wait_all(); - } - -#if cimg_display==0 - - //! Wait for any window event occuring in any opened CImgDisplay. - static void wait_all() { - return _no_display_exception(); - } - - //! Render image into internal display buffer. - /** - \param img Input image data to render. - \note - - Convert image data representation into the internal display buffer (architecture-dependent structure). - - The content of the associated window is not modified, until paint() is called. - - Should not be used for common CImgDisplay uses, since display() is more useful. - **/ - template<typename T> - CImgDisplay& render(const CImg<T>& img) { - return assign(img); - } - - //! Paint internal display buffer on associated window. - /** - \note - - Update the content of the associated window with the internal display buffer, e.g. after a render() call. - - Should not be used for common CImgDisplay uses, since display() is more useful. - **/ - CImgDisplay& paint() { - return assign(); - } - - - //! Take a snapshot of the current screen content. - /** - \param x0 X-coordinate of the upper left corner. - \param y0 Y-coordinate of the upper left corner. - \param x1 X-coordinate of the lower right corner. - \param y1 Y-coordinate of the lower right corner. - \param[out] img Output screenshot. Can be empty on input - **/ - template<typename T> - static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) { - cimg::unused(x0,y0,x1,y1,&img); - _no_display_exception(); - } - - //! Take a snapshot of the associated window content. - /** - \param[out] img Output snapshot. Can be empty on input. - **/ - template<typename T> - const CImgDisplay& snapshot(CImg<T>& img) const { - cimg::unused(img); - _no_display_exception(); - return *this; - } -#endif - - // X11-based implementation - //-------------------------- -#if cimg_display==1 - - Atom _wm_window_atom, _wm_protocol_atom; - Window _window, _background_window; - Colormap _colormap; - XImage *_image; - void *_data; -#ifdef cimg_use_xshm - XShmSegmentInfo *_shminfo; -#endif - - static int screen_width() { - Display *const dpy = cimg::X11_attr().display; - int res = 0; - if (!dpy) { - Display *const _dpy = XOpenDisplay(0); - if (!_dpy) - throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display."); - res = DisplayWidth(_dpy,DefaultScreen(_dpy)); - XCloseDisplay(_dpy); - } else { -#ifdef cimg_use_xrandr - if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) - res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width; - else res = DisplayWidth(dpy,DefaultScreen(dpy)); -#else - res = DisplayWidth(dpy,DefaultScreen(dpy)); -#endif - } - return res; - } - - static int screen_height() { - Display *const dpy = cimg::X11_attr().display; - int res = 0; - if (!dpy) { - Display *const _dpy = XOpenDisplay(0); - if (!_dpy) - throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display."); - res = DisplayHeight(_dpy,DefaultScreen(_dpy)); - XCloseDisplay(_dpy); - } else { -#ifdef cimg_use_xrandr - if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) - res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height; - else res = DisplayHeight(dpy,DefaultScreen(dpy)); -#else - res = DisplayHeight(dpy,DefaultScreen(dpy)); -#endif - } - return res; - } - - static void wait_all() { - if (!cimg::X11_attr().display) return; - pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex); - pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex); - pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex); - } - - void _handle_events(const XEvent *const pevent) { - Display *const dpy = cimg::X11_attr().display; - XEvent event = *pevent; - switch (event.type) { - case ClientMessage : { - if ((int)event.xclient.message_type==(int)_wm_protocol_atom && - (int)event.xclient.data.l[0]==(int)_wm_window_atom) { - XUnmapWindow(cimg::X11_attr().display,_window); - _is_closed = _is_event = true; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } - } break; - case ConfigureNotify : { - while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {} - const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height; - const int nx = event.xconfigure.x, ny = event.xconfigure.y; - if (nw && nh && (nw!=_window_width || nh!=_window_height)) { - _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1; - XResizeWindow(dpy,_window,_window_width,_window_height); - _is_resized = _is_event = true; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } - if (nx!=_window_x || ny!=_window_y) { - _window_x = nx; _window_y = ny; _is_moved = _is_event = true; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } - } break; - case Expose : { - while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {} - _paint(false); - if (_is_fullscreen) { - XWindowAttributes attr; - XGetWindowAttributes(dpy,_window,&attr); - while (attr.map_state!=IsViewable) XSync(dpy,0); - XSetInputFocus(dpy,_window,RevertToParent,CurrentTime); - } - } break; - case ButtonPress : { - do { - _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; - if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; - switch (event.xbutton.button) { - case 1 : set_button(1); break; - case 3 : set_button(2); break; - case 2 : set_button(3); break; - } - } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event)); - } break; - case ButtonRelease : { - do { - _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; - if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; - switch (event.xbutton.button) { - case 1 : set_button(1,false); break; - case 3 : set_button(2,false); break; - case 2 : set_button(3,false); break; - case 4 : set_wheel(1); break; - case 5 : set_wheel(-1); break; - } - } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event)); - } break; - case KeyPress : { - char tmp = 0; KeySym ksym; - XLookupString(&event.xkey,&tmp,1,&ksym,0); - set_key((unsigned int)ksym,true); - } break; - case KeyRelease : { - char keys_return[32]; // Check that the key has been physically unpressed - XQueryKeymap(dpy,keys_return); - const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8; - const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1; - if (!is_key_pressed) { - char tmp = 0; KeySym ksym; - XLookupString(&event.xkey,&tmp,1,&ksym,0); - set_key((unsigned int)ksym,false); - } - } break; - case EnterNotify: { - while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {} - _mouse_x = event.xmotion.x; - _mouse_y = event.xmotion.y; - if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; - } break; - case LeaveNotify : { - while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {} - _mouse_x = _mouse_y = -1; _is_event = true; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } break; - case MotionNotify : { - while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {} - _mouse_x = event.xmotion.x; - _mouse_y = event.xmotion.y; - if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; - _is_event = true; - pthread_cond_broadcast(&cimg::X11_attr().wait_event); - } break; - } - } - - static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows - Display *const dpy = cimg::X11_attr().display; - XEvent event; - pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); - if (!arg) for ( ; ; ) { - cimg_lock_display(); - bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event); - if (!event_flag) event_flag = XCheckMaskEvent(dpy, - ExposureMask | StructureNotifyMask | ButtonPressMask | - KeyPressMask | PointerMotionMask | EnterWindowMask | - LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event); - if (event_flag) - for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i) - if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) - cimg::X11_attr().wins[i]->_handle_events(&event); - cimg_unlock_display(); - pthread_testcancel(); - cimg::sleep(8); - } - return 0; - } - - void _set_colormap(Colormap& _colormap, const unsigned int dim) { - XColor *const colormap = new XColor[256]; - switch (dim) { - case 1 : { // colormap for greyscale images - for (unsigned int index = 0; index<256; ++index) { - colormap[index].pixel = index; - colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8); - colormap[index].flags = DoRed | DoGreen | DoBlue; - } - } break; - case 2 : { // colormap for RG images - for (unsigned int index = 0, r = 8; r<256; r+=16) - for (unsigned int g = 8; g<256; g+=16) { - colormap[index].pixel = index; - colormap[index].red = colormap[index].blue = (unsigned short)(r<<8); - colormap[index].green = (unsigned short)(g<<8); - colormap[index++].flags = DoRed | DoGreen | DoBlue; - } - } break; - default : { // colormap for RGB images - for (unsigned int index = 0, r = 16; r<256; r+=32) - for (unsigned int g = 16; g<256; g+=32) - for (unsigned int b = 32; b<256; b+=64) { - colormap[index].pixel = index; - colormap[index].red = (unsigned short)(r<<8); - colormap[index].green = (unsigned short)(g<<8); - colormap[index].blue = (unsigned short)(b<<8); - colormap[index++].flags = DoRed | DoGreen | DoBlue; - } - } - } - XStoreColors(cimg::X11_attr().display,_colormap,colormap,256); - delete[] colormap; - } - - void _map_window() { - Display *const dpy = cimg::X11_attr().display; - bool is_exposed = false, is_mapped = false; - XWindowAttributes attr; - XEvent event; - XMapRaised(dpy,_window); - do { // Wait for the window to be mapped - XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event); - switch (event.type) { - case MapNotify : is_mapped = true; break; - case Expose : is_exposed = true; break; - } - } while (!is_exposed || !is_mapped); - do { // Wait for the window to be visible - XGetWindowAttributes(dpy,_window,&attr); - if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } - } while (attr.map_state!=IsViewable); - _window_x = attr.x; - _window_y = attr.y; - } - - void _paint(const bool wait_expose=true) { - if (_is_closed || !_image) return; - Display *const dpy = cimg::X11_attr().display; - if (wait_expose) { // Send an expose event sticked to display window to force repaint - XEvent event; - event.xexpose.type = Expose; - event.xexpose.serial = 0; - event.xexpose.send_event = 1; - event.xexpose.display = dpy; - event.xexpose.window = _window; - event.xexpose.x = 0; - event.xexpose.y = 0; - event.xexpose.width = width(); - event.xexpose.height = height(); - event.xexpose.count = 0; - XSendEvent(dpy,_window,0,0,&event); - } else { // Repaint directly (may be called from the expose event) - GC gc = DefaultGC(dpy,DefaultScreen(dpy)); -#ifdef cimg_use_xshm - if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1); - else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); -#else - XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); -#endif - } - } - - template<typename T> - void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) { - Display *const dpy = cimg::X11_attr().display; - cimg::unused(pixel_type); - -#ifdef cimg_use_xshm - if (_shminfo) { - XShmSegmentInfo *const nshminfo = new XShmSegmentInfo; - XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), - cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); - if (!nimage) { delete nshminfo; return; } - else { - nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777); - if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; } - else { - nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); - if (nshminfo->shmaddr==(char*)-1) { - shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; - } else { - nshminfo->readOnly = 0; - cimg::X11_attr().is_shm_enabled = true; - XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); - XShmAttach(dpy,nshminfo); - XFlush(dpy); - XSetErrorHandler(oldXErrorHandler); - if (!cimg::X11_attr().is_shm_enabled) { - shmdt(nshminfo->shmaddr); - shmctl(nshminfo->shmid,IPC_RMID,0); - XDestroyImage(nimage); - delete nshminfo; - return; - } else { - T *const ndata = (T*)nimage->data; - if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); - else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); - XShmDetach(dpy,_shminfo); - XDestroyImage(_image); - shmdt(_shminfo->shmaddr); - shmctl(_shminfo->shmid,IPC_RMID,0); - delete _shminfo; - _shminfo = nshminfo; - _image = nimage; - _data = (void*)ndata; - } - } - } - } - } else -#endif - { - T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T)); - if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); - else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); - _data = (void*)ndata; - XDestroyImage(_image); - _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), - cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0); - } - } - - void _init_fullscreen() { - if (!_is_fullscreen || _is_closed) return; - Display *const dpy = cimg::X11_attr().display; - _background_window = 0; - -#ifdef cimg_use_xrandr - int foo; - if (XRRQueryExtension(dpy,&foo,&foo)) { - XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation); - if (!cimg::X11_attr().resolutions) { - cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo); - cimg::X11_attr().nb_resolutions = (unsigned int)foo; - } - if (cimg::X11_attr().resolutions) { - cimg::X11_attr().curr_resolution = 0; - for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) { - const unsigned int - nw = (unsigned int)(cimg::X11_attr().resolutions[i].width), - nh = (unsigned int)(cimg::X11_attr().resolutions[i].height); - if (nw>=_width && nh>=_height && - nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) && - nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height)) - cimg::X11_attr().curr_resolution = i; - } - if (cimg::X11_attr().curr_resolution>0) { - XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); - XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy), - cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime); - XRRFreeScreenConfigInfo(config); - XSync(dpy,0); - } - } - } - if (!cimg::X11_attr().resolutions) - cimg::warn(_cimgdisplay_instance - "init_fullscreen(): Xrandr extension not supported by the X server.", - cimgdisplay_instance); -#endif - - const unsigned int sx = screen_width(), sy = screen_height(); - if (sx==_width && sy==_height) return; - XSetWindowAttributes winattr; - winattr.override_redirect = 1; - _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0, - InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); - const cimg_ulong buf_size = (cimg_ulong)sx*sy*(cimg::X11_attr().nb_bits==8?1: - (cimg::X11_attr().nb_bits==16?2:4)); - void *background_data = std::malloc(buf_size); - std::memset(background_data,0,buf_size); - XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, - ZPixmap,0,(char*)background_data,sx,sy,8,0); - XEvent event; - XSelectInput(dpy,_background_window,StructureNotifyMask); - XMapRaised(dpy,_background_window); - do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event); - while (event.type!=MapNotify); - GC gc = DefaultGC(dpy,DefaultScreen(dpy)); -#ifdef cimg_use_xshm - if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0); - else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy); -#else - XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy); -#endif - XWindowAttributes attr; - XGetWindowAttributes(dpy,_background_window,&attr); - while (attr.map_state!=IsViewable) XSync(dpy,0); - XDestroyImage(background_image); - } - - void _desinit_fullscreen() { - if (!_is_fullscreen) return; - Display *const dpy = cimg::X11_attr().display; - XUngrabKeyboard(dpy,CurrentTime); -#ifdef cimg_use_xrandr - if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) { - XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); - XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime); - XRRFreeScreenConfigInfo(config); - XSync(dpy,0); - cimg::X11_attr().curr_resolution = 0; - } -#endif - if (_background_window) XDestroyWindow(dpy,_background_window); - _background_window = 0; - _is_fullscreen = false; - } - - static int _assign_xshm(Display *dpy, XErrorEvent *error) { - cimg::unused(dpy,error); - cimg::X11_attr().is_shm_enabled = false; - return 0; - } - - void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - cimg::mutex(14); - - // Allocate space for window title - const char *const nptitle = ptitle?ptitle:""; - const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; - char *const tmp_title = s?new char[s]:0; - if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); - - // Destroy previous display window if existing - if (!is_empty()) assign(); - - // Open X11 display and retrieve graphical properties. - Display* &dpy = cimg::X11_attr().display; - if (!dpy) { - dpy = XOpenDisplay(0); - if (!dpy) - throw CImgDisplayException(_cimgdisplay_instance - "assign(): Failed to open X11 display.", - cimgdisplay_instance); - - cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy)); - if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && - cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32) - throw CImgDisplayException(_cimgdisplay_instance - "assign(): Invalid %u bits screen mode detected " - "(only 8, 16, 24 and 32 bits modes are managed).", - cimgdisplay_instance, - cimg::X11_attr().nb_bits); - XVisualInfo vtemplate; - vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy))); - int nb_visuals; - XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals); - if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true; - cimg::X11_attr().byte_order = ImageByteOrder(dpy); - XFree(vinfo); - - cimg_lock_display(); - cimg::X11_attr().events_thread = new pthread_t; - pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0); - } else cimg_lock_display(); - - // Set display variables. - _width = std::min(dimw,(unsigned int)screen_width()); - _height = std::min(dimh,(unsigned int)screen_height()); - _normalization = normalization_type<4?normalization_type:3; - _is_fullscreen = fullscreen_flag; - _window_x = _window_y = 0; - _is_closed = closed_flag; - _title = tmp_title; - flush(); - - // Create X11 window (and LUT, if 8bits display) - if (_is_fullscreen) { - if (!_is_closed) _init_fullscreen(); - const unsigned int sx = screen_width(), sy = screen_height(); - XSetWindowAttributes winattr; - winattr.override_redirect = 1; - _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0, - InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); - } else - _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L); - - XSelectInput(dpy,_window, - ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | - EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); - - XStoreName(dpy,_window,_title?_title:" "); - if (cimg::X11_attr().nb_bits==8) { - _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll); - _set_colormap(_colormap,3); - XSetWindowColormap(dpy,_window,_colormap); - } - - static const char *const _window_class = cimg_appname; - XClassHint *const window_class = XAllocClassHint(); - window_class->res_name = (char*)_window_class; - window_class->res_class = (char*)_window_class; - XSetClassHint(dpy,_window,window_class); - XFree(window_class); - - _window_width = _width; - _window_height = _height; - - // Create XImage -#ifdef cimg_use_xshm - _shminfo = 0; - if (XShmQueryExtension(dpy)) { - _shminfo = new XShmSegmentInfo; - _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, - ZPixmap,0,_shminfo,_width,_height); - if (!_image) { delete _shminfo; _shminfo = 0; } - else { - _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777); - if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; } - else { - _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0)); - if (_shminfo->shmaddr==(char*)-1) { - shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; - } else { - _shminfo->readOnly = 0; - cimg::X11_attr().is_shm_enabled = true; - XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); - XShmAttach(dpy,_shminfo); - XSync(dpy,0); - XSetErrorHandler(oldXErrorHandler); - if (!cimg::X11_attr().is_shm_enabled) { - shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); - delete _shminfo; _shminfo = 0; - } - } - } - } - } - if (!_shminfo) -#endif - { - const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1: - (cimg::X11_attr().nb_bits==16?2:4)); - _data = std::malloc(buf_size); - _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, - ZPixmap,0,(char*)_data,_width,_height,8,0); - } - - _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0); - _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0); - XSetWMProtocols(dpy,_window,&_wm_window_atom,1); - - if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime); - cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this; - if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); } - cimg_unlock_display(); - cimg::mutex(14,0); - } - - CImgDisplay& assign() { - if (is_empty()) return flush(); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - - // Remove display window from event thread list. - unsigned int i; - for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {} - for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1]; - --cimg::X11_attr().nb_wins; - - // Destroy window, image, colormap and title. - if (_is_fullscreen && !_is_closed) _desinit_fullscreen(); - XDestroyWindow(dpy,_window); - _window = 0; -#ifdef cimg_use_xshm - if (_shminfo) { - XShmDetach(dpy,_shminfo); - XDestroyImage(_image); - shmdt(_shminfo->shmaddr); - shmctl(_shminfo->shmid,IPC_RMID,0); - delete _shminfo; - _shminfo = 0; - } else -#endif - XDestroyImage(_image); - _data = 0; _image = 0; - if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap); - _colormap = 0; - XSync(dpy,0); - - // Reset display variables. - delete[] _title; - _width = _height = _normalization = _window_width = _window_height = 0; - _window_x = _window_y = 0; - _is_fullscreen = false; - _is_closed = true; - _min = _max = 0; - _title = 0; - flush(); - - cimg_unlock_display(); - return *this; - } - - CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - if (!dimw || !dimh) return assign(); - _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); - _min = _max = 0; - std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): - (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))* - (size_t)_width*_height); - return paint(); - } - - template<typename T> - CImgDisplay& assign(const CImg<T>& img, const char *const title=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - if (!img) return assign(); - CImg<T> tmp; - const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, - (img._height - 1)/2, - (img._depth - 1)/2)); - _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); - if (_normalization==2) _min = (float)nimg.min_max(_max); - return render(nimg).paint(); - } - - template<typename T> - CImgDisplay& assign(const CImgList<T>& list, const char *const title=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - if (!list) return assign(); - CImg<T> tmp; - const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, - (img._height - 1)/2, - (img._depth - 1)/2)); - _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); - if (_normalization==2) _min = (float)nimg.min_max(_max); - return render(nimg).paint(); - } - - CImgDisplay& assign(const CImgDisplay& disp) { - if (!disp) return assign(); - _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); - std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): - cimg::X11_attr().nb_bits==16?sizeof(unsigned short): - sizeof(unsigned int))*(size_t)_width*_height); - return paint(); - } - - CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { - if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); - if (is_empty()) return assign(nwidth,nheight); - Display *const dpy = cimg::X11_attr().display; - const unsigned int - tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100), - tmpdimy = (nheight>0)?nheight:(-nheight*height()/100), - dimx = tmpdimx?tmpdimx:1, - dimy = tmpdimy?tmpdimy:1; - if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { - show(); - cimg_lock_display(); - if (_window_width!=dimx || _window_height!=dimy) { - XWindowAttributes attr; - for (unsigned int i = 0; i<10; ++i) { - XResizeWindow(dpy,_window,dimx,dimy); - XGetWindowAttributes(dpy,_window,&attr); - if (attr.width==(int)dimx && attr.height==(int)dimy) break; - cimg::wait(5,&_timer); - } - } - if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) { - case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; - case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; - default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } - } - _window_width = _width = dimx; _window_height = _height = dimy; - cimg_unlock_display(); - } - _is_resized = false; - if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2); - if (force_redraw) return paint(); - return *this; - } - - CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { - if (is_empty()) return *this; - if (force_redraw) { - const cimg_ulong buf_size = (cimg_ulong)_width*_height* - (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); - void *image_data = std::malloc(buf_size); - std::memcpy(image_data,_data,buf_size); - assign(_width,_height,_title,_normalization,!_is_fullscreen,false); - std::memcpy(_data,image_data,buf_size); - std::free(image_data); - return paint(); - } - return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); - } - - CImgDisplay& show() { - if (is_empty() || !_is_closed) return *this; - cimg_lock_display(); - if (_is_fullscreen) _init_fullscreen(); - _map_window(); - _is_closed = false; - cimg_unlock_display(); - return paint(); - } - - CImgDisplay& close() { - if (is_empty() || _is_closed) return *this; - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - if (_is_fullscreen) _desinit_fullscreen(); - XUnmapWindow(dpy,_window); - _window_x = _window_y = -1; - _is_closed = true; - cimg_unlock_display(); - return *this; - } - - CImgDisplay& move(const int posx, const int posy) { - if (is_empty()) return *this; - if (_window_x!=posx || _window_y!=posy) { - show(); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XMoveWindow(dpy,_window,posx,posy); - _window_x = posx; _window_y = posy; - cimg_unlock_display(); - } - _is_moved = false; - return paint(); - } - - CImgDisplay& show_mouse() { - if (is_empty()) return *this; - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XUndefineCursor(dpy,_window); - cimg_unlock_display(); - return *this; - } - - CImgDisplay& hide_mouse() { - if (is_empty()) return *this; - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - static const char pix_data[8] = { 0 }; - XColor col; - col.red = col.green = col.blue = 0; - Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8); - Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0); - XFreePixmap(dpy,pix); - XDefineCursor(dpy,_window,cur); - cimg_unlock_display(); - return *this; - } - - CImgDisplay& set_mouse(const int posx, const int posy) { - if (is_empty() || _is_closed) return *this; - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy); - _mouse_x = posx; _mouse_y = posy; - _is_moved = false; - XSync(dpy,0); - cimg_unlock_display(); - return *this; - } - - CImgDisplay& set_title(const char *const format, ...) { - if (is_empty()) return *this; - char *const tmp = new char[1024]; - va_list ap; - va_start(ap, format); - cimg_vsnprintf(tmp,1024,format,ap); - va_end(ap); - if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } - delete[] _title; - const unsigned int s = (unsigned int)std::strlen(tmp) + 1; - _title = new char[s]; - std::memcpy(_title,tmp,s*sizeof(char)); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XStoreName(dpy,_window,tmp); - cimg_unlock_display(); - delete[] tmp; - return *this; - } - - template<typename T> - CImgDisplay& display(const CImg<T>& img) { - if (!img) - throw CImgArgumentException(_cimgdisplay_instance - "display(): Empty specified image.", - cimgdisplay_instance); - if (is_empty()) return assign(img); - return render(img).paint(false); - } - - CImgDisplay& paint(const bool wait_expose=true) { - if (is_empty()) return *this; - cimg_lock_display(); - _paint(wait_expose); - cimg_unlock_display(); - return *this; - } - - template<typename T> - CImgDisplay& render(const CImg<T>& img, const bool flag8=false) { - if (!img) - throw CImgArgumentException(_cimgdisplay_instance - "render(): Empty specified image.", - cimgdisplay_instance); - if (is_empty()) return *this; - if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, - (img._depth - 1)/2)); - if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) - return render(img.get_resize(_width,_height,1,-100,1)); - if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) { - static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256(); - return render(img.get_index(default_colormap,1,false)); - } - - const T - *data1 = img._data, - *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1, - *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1; - - if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); - cimg_lock_display(); - - if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) { - _min = _max = 0; - switch (cimg::X11_attr().nb_bits) { - case 8 : { // 256 colormap, no normalization - _set_colormap(_colormap,img._spectrum); - unsigned char - *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: - new unsigned char[(size_t)img._width*img._height], - *ptrd = (unsigned char*)ndata; - switch (img._spectrum) { - case 1 : - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - (*ptrd++) = (unsigned char)*(data1++); - break; - case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)*(data1++), - G = (unsigned char)*(data2++); - (*ptrd++) = (R&0xf0) | (G>>4); - } break; - default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)*(data1++), - G = (unsigned char)*(data2++), - B = (unsigned char)*(data3++); - (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); - } - } - if (ndata!=_data) { - _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); - delete[] ndata; - } - } break; - case 16 : { // 16 bits colors, no normalization - unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: - new unsigned short[(size_t)img._width*img._height]; - unsigned char *ptrd = (unsigned char*)ndata; - const unsigned int M = 248; - switch (img._spectrum) { - case 1 : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)*(data1++), G = val>>2; - ptrd[0] = (val&M) | (G>>3); - ptrd[1] = (G<<5) | (G>>1); - ptrd+=2; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)*(data1++), G = val>>2; - ptrd[0] = (G<<5) | (G>>1); - ptrd[1] = (val&M) | (G>>3); - ptrd+=2; - } - break; - case 2 : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); - ptrd[1] = (G<<5); - ptrd+=2; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - ptrd[0] = (G<<5); - ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); - ptrd+=2; - } - break; - default : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); - ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3); - ptrd+=2; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3); - ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); - ptrd+=2; - } - } - if (ndata!=_data) { - _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); - delete[] ndata; - } - } break; - default : { // 24 bits colors, no normalization - unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: - new unsigned int[(size_t)img._width*img._height]; - if (sizeof(int)==4) { // 32 bits int uses optimized version - unsigned int *ptrd = ndata; - switch (img._spectrum) { - case 1 : - if (cimg::X11_attr().byte_order==cimg::endianness()) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)*(data1++); - *(ptrd++) = (val<<16) | (val<<8) | val; - } - else - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)*(data1++); - *(ptrd++) = (val<<16) | (val<<8) | val; - } - break; - case 2 : - if (cimg::X11_attr().byte_order==cimg::endianness()) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); - else - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); - break; - default : - if (cimg::X11_attr().byte_order==cimg::endianness()) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | - (unsigned char)*(data3++); - else - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | - ((unsigned char)*(data1++)<<8); - } - } else { - unsigned char *ptrd = (unsigned char*)ndata; - switch (img._spectrum) { - case 1 : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = 0; - ptrd[1] = (unsigned char)*(data1++); - ptrd[2] = 0; - ptrd[3] = 0; - ptrd+=4; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = 0; - ptrd[1] = 0; - ptrd[2] = (unsigned char)*(data1++); - ptrd[3] = 0; - ptrd+=4; - } - break; - case 2 : - if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = 0; - ptrd[1] = (unsigned char)*(data2++); - ptrd[2] = (unsigned char)*(data1++); - ptrd[3] = 0; - ptrd+=4; - } - break; - default : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = 0; - ptrd[1] = (unsigned char)*(data1++); - ptrd[2] = (unsigned char)*(data2++); - ptrd[3] = (unsigned char)*(data3++); - ptrd+=4; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = (unsigned char)*(data3++); - ptrd[1] = (unsigned char)*(data2++); - ptrd[2] = (unsigned char)*(data1++); - ptrd[3] = 0; - ptrd+=4; - } - } - } - if (ndata!=_data) { - _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); - delete[] ndata; - } - } - } - } else { - if (_normalization==3) { - if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max); - else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); } - } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); - const float delta = _max - _min, mm = 255/(delta?delta:1.f); - switch (cimg::X11_attr().nb_bits) { - case 8 : { // 256 colormap, with normalization - _set_colormap(_colormap,img._spectrum); - unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: - new unsigned char[(size_t)img._width*img._height]; - unsigned char *ptrd = (unsigned char*)ndata; - switch (img._spectrum) { - case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char R = (unsigned char)((*(data1++) - _min)*mm); - *(ptrd++) = R; - } break; - case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)((*(data1++) - _min)*mm), - G = (unsigned char)((*(data2++) - _min)*mm); - (*ptrd++) = (R&0xf0) | (G>>4); - } break; - default : - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)((*(data1++) - _min)*mm), - G = (unsigned char)((*(data2++) - _min)*mm), - B = (unsigned char)((*(data3++) - _min)*mm); - *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); - } - } - if (ndata!=_data) { - _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); - delete[] ndata; - } - } break; - case 16 : { // 16 bits colors, with normalization - unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: - new unsigned short[(size_t)img._width*img._height]; - unsigned char *ptrd = (unsigned char*)ndata; - const unsigned int M = 248; - switch (img._spectrum) { - case 1 : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; - ptrd[0] = (val&M) | (G>>3); - ptrd[1] = (G<<5) | (val>>3); - ptrd+=2; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; - ptrd[0] = (G<<5) | (val>>3); - ptrd[1] = (val&M) | (G>>3); - ptrd+=2; - } - break; - case 2 : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; - ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); - ptrd[1] = (G<<5); - ptrd+=2; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; - ptrd[0] = (G<<5); - ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); - ptrd+=2; - } - break; - default : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; - ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); - ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); - ptrd+=2; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; - ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); - ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); - ptrd+=2; - } - } - if (ndata!=_data) { - _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); - delete[] ndata; - } - } break; - default : { // 24 bits colors, with normalization - unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: - new unsigned int[(size_t)img._width*img._height]; - if (sizeof(int)==4) { // 32 bits int uses optimized version - unsigned int *ptrd = ndata; - switch (img._spectrum) { - case 1 : - if (cimg::X11_attr().byte_order==cimg::endianness()) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); - *(ptrd++) = (val<<16) | (val<<8) | val; - } - else - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); - *(ptrd++) = (val<<24) | (val<<16) | (val<<8); - } - break; - case 2 : - if (cimg::X11_attr().byte_order==cimg::endianness()) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = - ((unsigned char)((*(data1++) - _min)*mm)<<16) | - ((unsigned char)((*(data2++) - _min)*mm)<<8); - else - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = - ((unsigned char)((*(data2++) - _min)*mm)<<16) | - ((unsigned char)((*(data1++) - _min)*mm)<<8); - break; - default : - if (cimg::X11_attr().byte_order==cimg::endianness()) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = - ((unsigned char)((*(data1++) - _min)*mm)<<16) | - ((unsigned char)((*(data2++) - _min)*mm)<<8) | - (unsigned char)((*(data3++) - _min)*mm); - else - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) - *(ptrd++) = - ((unsigned char)((*(data3++) - _min)*mm)<<24) | - ((unsigned char)((*(data2++) - _min)*mm)<<16) | - ((unsigned char)((*(data1++) - _min)*mm)<<8); - } - } else { - unsigned char *ptrd = (unsigned char*)ndata; - switch (img._spectrum) { - case 1 : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); - ptrd[0] = 0; - ptrd[1] = val; - ptrd[2] = val; - ptrd[3] = val; - ptrd+=4; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); - ptrd[0] = val; - ptrd[1] = val; - ptrd[2] = val; - ptrd[3] = 0; - ptrd+=4; - } - break; - case 2 : - if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = 0; - ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); - ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); - ptrd[3] = 0; - ptrd+=4; - } - break; - default : - if (cimg::X11_attr().byte_order) - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = 0; - ptrd[1] = (unsigned char)((*(data1++) - _min)*mm); - ptrd[2] = (unsigned char)((*(data2++) - _min)*mm); - ptrd[3] = (unsigned char)((*(data3++) - _min)*mm); - ptrd+=4; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ptrd[0] = (unsigned char)((*(data3++) - _min)*mm); - ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); - ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); - ptrd[3] = 0; - ptrd+=4; - } - } - } - if (ndata!=_data) { - _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); - delete[] ndata; - } - } - } - } - cimg_unlock_display(); - return *this; - } - - template<typename T> - static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) { - img.assign(); - Display *dpy = cimg::X11_attr().display; - cimg_lock_display(); - if (!dpy) { - dpy = XOpenDisplay(0); - if (!dpy) - throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display."); - } - Window root = DefaultRootWindow(dpy); - XWindowAttributes gwa; - XGetWindowAttributes(dpy,root,&gwa); - const int width = gwa.width, height = gwa.height; - int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; - if (_x0>_x1) cimg::swap(_x0,_x1); - if (_y0>_y1) cimg::swap(_y0,_y1); - - XImage *image = 0; - if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) { - _x0 = std::max(_x0,0); - _y0 = std::max(_y0,0); - _x1 = std::min(_x1,width - 1); - _y1 = std::min(_y1,height - 1); - image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap); - - if (image) { - const unsigned long - red_mask = image->red_mask, - green_mask = image->green_mask, - blue_mask = image->blue_mask; - img.assign(image->width,image->height,1,3); - T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2); - cimg_forXY(img,x,y) { - const unsigned long pixel = XGetPixel(image,x,y); - *(pR++) = (T)((pixel & red_mask)>>16); - *(pG++) = (T)((pixel & green_mask)>>8); - *(pB++) = (T)(pixel & blue_mask); - } - XDestroyImage(image); - } - } - if (!cimg::X11_attr().display) XCloseDisplay(dpy); - cimg_unlock_display(); - if (img.is_empty()) - throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot " - "with coordinates (%d,%d)-(%d,%d).", - x0,y0,x1,y1); - } - - template<typename T> - const CImgDisplay& snapshot(CImg<T>& img) const { - if (is_empty()) { img.assign(); return *this; } - const unsigned char *ptrs = (unsigned char*)_data; - img.assign(_width,_height,1,3); - T - *data1 = img.data(0,0,0,0), - *data2 = img.data(0,0,0,1), - *data3 = img.data(0,0,0,2); - if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); - switch (cimg::X11_attr().nb_bits) { - case 8 : { - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = *(ptrs++); - *(data1++) = (T)(val&0xe0); - *(data2++) = (T)((val&0x1c)<<3); - *(data3++) = (T)(val<<6); - } - } break; - case 16 : { - if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - val0 = ptrs[0], - val1 = ptrs[1]; - ptrs+=2; - *(data1++) = (T)(val0&0xf8); - *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5)); - *(data3++) = (T)(val1<<3); - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned short - val0 = ptrs[0], - val1 = ptrs[1]; - ptrs+=2; - *(data1++) = (T)(val1&0xf8); - *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5)); - *(data3++) = (T)(val0<<3); - } - } break; - default : { - if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - ++ptrs; - *(data1++) = (T)ptrs[0]; - *(data2++) = (T)ptrs[1]; - *(data3++) = (T)ptrs[2]; - ptrs+=3; - } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - *(data3++) = (T)ptrs[0]; - *(data2++) = (T)ptrs[1]; - *(data1++) = (T)ptrs[2]; - ptrs+=3; - ++ptrs; - } - } - } - return *this; - } - - // Windows-based implementation. - //------------------------------- -#elif cimg_display==2 - - bool _is_mouse_tracked, _is_cursor_visible; - HANDLE _thread, _is_created, _mutex; - HWND _window, _background_window; - CLIENTCREATESTRUCT _ccs; - unsigned int *_data; - DEVMODE _curr_mode; - BITMAPINFO _bmi; - HDC _hdc; - - static int screen_width() { - DEVMODE mode; - mode.dmSize = sizeof(DEVMODE); - mode.dmDriverExtra = 0; - EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); - return (int)mode.dmPelsWidth; - } - - static int screen_height() { - DEVMODE mode; - mode.dmSize = sizeof(DEVMODE); - mode.dmDriverExtra = 0; - EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); - return (int)mode.dmPelsHeight; - } - - static void wait_all() { - WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE); - } - - static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) { -#ifdef _WIN64 - CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); -#else - CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); -#endif - MSG st_msg; - switch (msg) { - case WM_CLOSE : - disp->_mouse_x = disp->_mouse_y = -1; - disp->_window_x = disp->_window_y = 0; - disp->set_button().set_key(0).set_key(0,false)._is_closed = true; - ReleaseMutex(disp->_mutex); - ShowWindow(disp->_window,SW_HIDE); - disp->_is_event = true; - SetEvent(cimg::Win32_attr().wait_event); - return 0; - case WM_SIZE : { - while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} - WaitForSingleObject(disp->_mutex,INFINITE); - const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); - if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) { - disp->_window_width = nw; - disp->_window_height = nh; - disp->_mouse_x = disp->_mouse_y = -1; - disp->_is_resized = disp->_is_event = true; - SetEvent(cimg::Win32_attr().wait_event); - } - ReleaseMutex(disp->_mutex); - } break; - case WM_MOVE : { - while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} - WaitForSingleObject(disp->_mutex,INFINITE); - const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); - if (nx!=disp->_window_x || ny!=disp->_window_y) { - disp->_window_x = nx; - disp->_window_y = ny; - disp->_is_moved = disp->_is_event = true; - SetEvent(cimg::Win32_attr().wait_event); - } - ReleaseMutex(disp->_mutex); - } break; - case WM_PAINT : - disp->paint(); - cimg_lock_display(); - if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); - cimg_unlock_display(); - break; - case WM_ERASEBKGND : - // return 0; - break; - case WM_KEYDOWN : - disp->set_key((unsigned int)wParam); - SetEvent(cimg::Win32_attr().wait_event); - break; - case WM_KEYUP : - disp->set_key((unsigned int)wParam,false); - SetEvent(cimg::Win32_attr().wait_event); - break; - case WM_MOUSEMOVE : { - while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {} - disp->_mouse_x = LOWORD(lParam); - disp->_mouse_y = HIWORD(lParam); -#if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT) - if (!disp->_is_mouse_tracked) { - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = disp->_window; - if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true; - } -#endif - if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height()) - disp->_mouse_x = disp->_mouse_y = -1; - disp->_is_event = true; - SetEvent(cimg::Win32_attr().wait_event); - cimg_lock_display(); - if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); - cimg_unlock_display(); - } break; - case WM_MOUSELEAVE : { - disp->_mouse_x = disp->_mouse_y = -1; - disp->_is_mouse_tracked = false; - cimg_lock_display(); - while (ShowCursor(TRUE)<0) {} - cimg_unlock_display(); - } break; - case WM_LBUTTONDOWN : - disp->set_button(1); - SetEvent(cimg::Win32_attr().wait_event); - break; - case WM_RBUTTONDOWN : - disp->set_button(2); - SetEvent(cimg::Win32_attr().wait_event); - break; - case WM_MBUTTONDOWN : - disp->set_button(3); - SetEvent(cimg::Win32_attr().wait_event); - break; - case WM_LBUTTONUP : - disp->set_button(1,false); - SetEvent(cimg::Win32_attr().wait_event); - break; - case WM_RBUTTONUP : - disp->set_button(2,false); - SetEvent(cimg::Win32_attr().wait_event); - break; - case WM_MBUTTONUP : - disp->set_button(3,false); - SetEvent(cimg::Win32_attr().wait_event); - break; - case 0x020A : // WM_MOUSEWHEEL: - disp->set_wheel((int)((short)HIWORD(wParam))/120); - SetEvent(cimg::Win32_attr().wait_event); - } - return DefWindowProc(window,msg,wParam,lParam); - } - - static DWORD WINAPI _events_thread(void* arg) { - CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]); - const char *const title = (const char*)(((void**)arg)[1]); - MSG msg; - delete[] (void**)arg; - disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - disp->_bmi.bmiHeader.biWidth = disp->width(); - disp->_bmi.bmiHeader.biHeight = -disp->height(); - disp->_bmi.bmiHeader.biPlanes = 1; - disp->_bmi.bmiHeader.biBitCount = 32; - disp->_bmi.bmiHeader.biCompression = BI_RGB; - disp->_bmi.bmiHeader.biSizeImage = 0; - disp->_bmi.bmiHeader.biXPelsPerMeter = 1; - disp->_bmi.bmiHeader.biYPelsPerMeter = 1; - disp->_bmi.bmiHeader.biClrUsed = 0; - disp->_bmi.bmiHeader.biClrImportant = 0; - disp->_data = new unsigned int[(size_t)disp->_width*disp->_height]; - if (!disp->_is_fullscreen) { // Normal window - RECT rect; - rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int - border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2), - border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1); - disp->_window = CreateWindowA("MDICLIENT",title?title:" ", - WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT, - disp->_width + 2*border1, disp->_height + border1 + border2, - 0,0,0,&(disp->_ccs)); - if (!disp->_is_closed) { - GetWindowRect(disp->_window,&rect); - disp->_window_x = rect.left + border1; - disp->_window_y = rect.top + border2; - } else disp->_window_x = disp->_window_y = 0; - } else { // Fullscreen window - const unsigned int - sx = (unsigned int)screen_width(), - sy = (unsigned int)screen_height(); - disp->_window = CreateWindowA("MDICLIENT",title?title:" ", - WS_POPUP | (disp->_is_closed?0:WS_VISIBLE), - (sx - disp->_width)/2, - (sy - disp->_height)/2, - disp->_width,disp->_height,0,0,0,&(disp->_ccs)); - disp->_window_x = disp->_window_y = 0; - } - SetForegroundWindow(disp->_window); - disp->_hdc = GetDC(disp->_window); - disp->_window_width = disp->_width; - disp->_window_height = disp->_height; - disp->flush(); -#ifdef _WIN64 - SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp); - SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events); -#else - SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp); - SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events); -#endif - SetEvent(disp->_is_created); - while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg); - return 0; - } - - CImgDisplay& _update_window_pos() { - if (_is_closed) _window_x = _window_y = -1; - else { - RECT rect; - rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int - border1 = (int)((rect.right - rect.left + 1 - _width)/2), - border2 = (int)(rect.bottom - rect.top + 1 - _height - border1); - GetWindowRect(_window,&rect); - _window_x = rect.left + border1; - _window_y = rect.top + border2; - } - return *this; - } - - void _init_fullscreen() { - _background_window = 0; - if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0; - else { - DEVMODE mode; - unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; - for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { - const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; - if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { - bestbpp = mode.dmBitsPerPel; - ibest = imode; - bw = nw; bh = nh; - } - } - if (bestbpp) { - _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0; - EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode); - EnumDisplaySettings(0,ibest,&mode); - ChangeDisplaySettings(&mode,0); - } else _curr_mode.dmSize = 0; - - const unsigned int - sx = (unsigned int)screen_width(), - sy = (unsigned int)screen_height(); - if (sx!=_width || sy!=_height) { - CLIENTCREATESTRUCT background_ccs; - _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs); - SetForegroundWindow(_background_window); - } - } - } - - void _desinit_fullscreen() { - if (!_is_fullscreen) return; - if (_background_window) DestroyWindow(_background_window); - _background_window = 0; - if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0); - _is_fullscreen = false; - } - - CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - - // Allocate space for window title - const char *const nptitle = ptitle?ptitle:""; - const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; - char *const tmp_title = s?new char[s]:0; - if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); - - // Destroy previous window if existing - if (!is_empty()) assign(); - - // Set display variables - _width = std::min(dimw,(unsigned int)screen_width()); - _height = std::min(dimh,(unsigned int)screen_height()); - _normalization = normalization_type<4?normalization_type:3; - _is_fullscreen = fullscreen_flag; - _window_x = _window_y = 0; - _is_closed = closed_flag; - _is_cursor_visible = true; - _is_mouse_tracked = false; - _title = tmp_title; - flush(); - if (_is_fullscreen) _init_fullscreen(); - - // Create event thread - void *const arg = (void*)(new void*[2]); - ((void**)arg)[0] = (void*)this; - ((void**)arg)[1] = (void*)_title; - _mutex = CreateMutex(0,FALSE,0); - _is_created = CreateEvent(0,FALSE,FALSE,0); - _thread = CreateThread(0,0,_events_thread,arg,0,0); - WaitForSingleObject(_is_created,INFINITE); - return *this; - } - - CImgDisplay& assign() { - if (is_empty()) return flush(); - DestroyWindow(_window); - TerminateThread(_thread,0); - delete[] _data; - delete[] _title; - _data = 0; - _title = 0; - if (_is_fullscreen) _desinit_fullscreen(); - _width = _height = _normalization = _window_width = _window_height = 0; - _window_x = _window_y = 0; - _is_fullscreen = false; - _is_closed = true; - _min = _max = 0; - _title = 0; - flush(); - return *this; - } - - CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - if (!dimw || !dimh) return assign(); - _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); - _min = _max = 0; - std::memset(_data,0,sizeof(unsigned int)*_width*_height); - return paint(); - } - - template<typename T> - CImgDisplay& assign(const CImg<T>& img, const char *const title=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - if (!img) return assign(); - CImg<T> tmp; - const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, - (img._height - 1)/2, - (img._depth - 1)/2)); - _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); - if (_normalization==2) _min = (float)nimg.min_max(_max); - return display(nimg); - } - - template<typename T> - CImgDisplay& assign(const CImgList<T>& list, const char *const title=0, - const unsigned int normalization_type=3, - const bool fullscreen_flag=false, const bool closed_flag=false) { - if (!list) return assign(); - CImg<T> tmp; - const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, - (img._height - 1)/2, - (img._depth - 1)/2)); - _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); - if (_normalization==2) _min = (float)nimg.min_max(_max); - return display(nimg); - } - - CImgDisplay& assign(const CImgDisplay& disp) { - if (!disp) return assign(); - _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); - std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height); - return paint(); - } - - CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { - if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); - if (is_empty()) return assign(nwidth,nheight); - const unsigned int - tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100), - tmpdimy = (nheight>0)?nheight:(-nheight*_height/100), - dimx = tmpdimx?tmpdimx:1, - dimy = tmpdimy?tmpdimy:1; - if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { - if (_window_width!=dimx || _window_height!=dimy) { - RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1; - SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); - } - if (_width!=dimx || _height!=dimy) { - unsigned int *const ndata = new unsigned int[dimx*dimy]; - if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy); - else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); - delete[] _data; - _data = ndata; - _bmi.bmiHeader.biWidth = (LONG)dimx; - _bmi.bmiHeader.biHeight = -(int)dimy; - _width = dimx; - _height = dimy; - } - _window_width = dimx; _window_height = dimy; - show(); - } - _is_resized = false; - if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2); - if (force_redraw) return paint(); - return *this; - } - - CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { - if (is_empty()) return *this; - if (force_redraw) { - const cimg_ulong buf_size = (cimg_ulong)_width*_height*4; - void *odata = std::malloc(buf_size); - if (odata) { - std::memcpy(odata,_data,buf_size); - assign(_width,_height,_title,_normalization,!_is_fullscreen,false); - std::memcpy(_data,odata,buf_size); - std::free(odata); - } - return paint(); - } - return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); - } - - CImgDisplay& show() { - if (is_empty() || !_is_closed) return *this; - _is_closed = false; - if (_is_fullscreen) _init_fullscreen(); - ShowWindow(_window,SW_SHOW); - _update_window_pos(); - return paint(); - } - - CImgDisplay& close() { - if (is_empty() || _is_closed) return *this; - _is_closed = true; - if (_is_fullscreen) _desinit_fullscreen(); - ShowWindow(_window,SW_HIDE); - _window_x = _window_y = 0; - return *this; - } - - CImgDisplay& move(const int posx, const int posy) { - if (is_empty()) return *this; - if (_window_x!=posx || _window_y!=posy) { - if (!_is_fullscreen) { - RECT rect; - rect.left = rect.top = 0; rect.right = (LONG)_window_width - 1; rect.bottom = (LONG)_window_height - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int - border1 = (int)((rect.right - rect.left + 1 -_width)/2), - border2 = (int)(rect.bottom - rect.top + 1 - _height - border1); - SetWindowPos(_window,0,posx - border1,posy - border2,0,0,SWP_NOSIZE | SWP_NOZORDER); - } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); - _window_x = posx; - _window_y = posy; - show(); - } - _is_moved = false; - return *this; - } - - CImgDisplay& show_mouse() { - if (is_empty()) return *this; - _is_cursor_visible = true; - return *this; - } - - CImgDisplay& hide_mouse() { - if (is_empty()) return *this; - _is_cursor_visible = false; - return *this; - } - - CImgDisplay& set_mouse(const int posx, const int posy) { - if (is_empty() || _is_closed || posx<0 || posy<0) return *this; - _update_window_pos(); - const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy); - if (res) { _mouse_x = posx; _mouse_y = posy; } - return *this; - } - - CImgDisplay& set_title(const char *const format, ...) { - if (is_empty()) return *this; - char *const tmp = new char[1024]; - va_list ap; - va_start(ap, format); - cimg_vsnprintf(tmp,1024,format,ap); - va_end(ap); - if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } - delete[] _title; - const unsigned int s = (unsigned int)std::strlen(tmp) + 1; - _title = new char[s]; - std::memcpy(_title,tmp,s*sizeof(char)); - SetWindowTextA(_window, tmp); - delete[] tmp; - return *this; - } - - template<typename T> - CImgDisplay& display(const CImg<T>& img) { - if (!img) - throw CImgArgumentException(_cimgdisplay_instance - "display(): Empty specified image.", - cimgdisplay_instance); - if (is_empty()) return assign(img); - return render(img).paint(); - } - - CImgDisplay& paint() { - if (_is_closed) return *this; - WaitForSingleObject(_mutex,INFINITE); - SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS); - ReleaseMutex(_mutex); - return *this; - } - - template<typename T> - CImgDisplay& render(const CImg<T>& img) { - if (!img) - throw CImgArgumentException(_cimgdisplay_instance - "render(): Empty specified image.", - cimgdisplay_instance); - - if (is_empty()) return *this; - if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, - (img._depth - 1)/2)); - - const T - *data1 = img._data, - *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1, - *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1; - - WaitForSingleObject(_mutex,INFINITE); - unsigned int - *const ndata = (img._width==_width && img._height==_height)?_data: - new unsigned int[(size_t)img._width*img._height], - *ptrd = ndata; - - if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) { - _min = _max = 0; - switch (img._spectrum) { - case 1 : { - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)*(data1++); - *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); - } - } break; - case 2 : { - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)*(data1++), - G = (unsigned char)*(data2++); - *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); - } - } break; - default : { - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)*(data1++), - G = (unsigned char)*(data2++), - B = (unsigned char)*(data3++); - *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); - } - } - } - } else { - if (_normalization==3) { - if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max); - else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); } - } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); - const float delta = _max - _min, mm = 255/(delta?delta:1.f); - switch (img._spectrum) { - case 1 : { - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); - *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); - } - } break; - case 2 : { - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)((*(data1++) - _min)*mm), - G = (unsigned char)((*(data2++) - _min)*mm); - *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); - } - } break; - default : { - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned char - R = (unsigned char)((*(data1++) - _min)*mm), - G = (unsigned char)((*(data2++) - _min)*mm), - B = (unsigned char)((*(data3++) - _min)*mm); - *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); - } - } - } - } - if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; } - ReleaseMutex(_mutex); - return *this; - } - - template<typename T> - static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) { - img.assign(); - HDC hScreen = GetDC(GetDesktopWindow()); - if (hScreen) { - const int - width = GetDeviceCaps(hScreen,HORZRES), - height = GetDeviceCaps(hScreen,VERTRES); - int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; - if (_x0>_x1) cimg::swap(_x0,_x1); - if (_y0>_y1) cimg::swap(_y0,_y1); - if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) { - _x0 = std::max(_x0,0); - _y0 = std::max(_y0,0); - _x1 = std::min(_x1,width - 1); - _y1 = std::min(_y1,height - 1); - const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1; - HDC hdcMem = CreateCompatibleDC(hScreen); - if (hdcMem) { - HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh); - if (hBitmap) { - HGDIOBJ hOld = SelectObject(hdcMem,hBitmap); - if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) { - BITMAPINFOHEADER bmi; - bmi.biSize = sizeof(BITMAPINFOHEADER); - bmi.biWidth = bw; - bmi.biHeight = -bh; - bmi.biPlanes = 1; - bmi.biBitCount = 32; - bmi.biCompression = BI_RGB; - bmi.biSizeImage = 0; - bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0; - bmi.biClrUsed = bmi.biClrImportant = 0; - unsigned char *buf = new unsigned char[4*bw*bh]; - if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) { - img.assign(bw,bh,1,3); - const unsigned char *ptrs = buf; - T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2); - cimg_forXY(img,x,y) { - *(pR++) = (T)ptrs[2]; - *(pG++) = (T)ptrs[1]; - *(pB++) = (T)ptrs[0]; - ptrs+=4; - } - } - delete[] buf; - } - DeleteObject(hBitmap); - } - DeleteDC(hdcMem); - } - } - ReleaseDC(GetDesktopWindow(),hScreen); - } - if (img.is_empty()) - throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot " - "with coordinates (%d,%d)-(%d,%d).", - x0,y0,x1,y1); - } - - template<typename T> - const CImgDisplay& snapshot(CImg<T>& img) const { - if (is_empty()) { img.assign(); return *this; } - const unsigned int *ptrs = _data; - img.assign(_width,_height,1,3); - T - *data1 = img.data(0,0,0,0), - *data2 = img.data(0,0,0,1), - *data3 = img.data(0,0,0,2); - for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { - const unsigned int val = *(ptrs++); - *(data1++) = (T)(unsigned char)(val>>16); - *(data2++) = (T)(unsigned char)((val>>8)&0xFF); - *(data3++) = (T)(unsigned char)(val&0xFF); - } - return *this; - } -#endif - - //@} - }; - - /* - #-------------------------------------- - # - # - # - # Definition of the CImg<T> structure - # - # - # - #-------------------------------------- - */ - - //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. - /** - This is the main class of the %CImg Library. It declares and constructs - an image, allows access to its pixel values, and is able to perform various image operations. - - \par Image representation - - A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels, - each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth - and number of channels. - Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>, - while the number of channels is rather used as a vector-valued dimension - (it may describe the R,G,B color channels for instance). - If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>. - - Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels, - as well as images with less dimensions (1D scalar signal, 2D color images, ...). - Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. - - Concerning the pixel value type \c T: - fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int, - unsigned long, long, float, double, ... </tt>. - Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images, - while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt> - images that have floating-point pixel values. The default value for the template T is \c float. - Using your own template types may be possible. However, you will certainly have to define the complete set - of arithmetic and logical operators for your class. - - \par Image structure - - The \c CImg<T> structure contains \e six fields: - - \c _width defines the number of \a columns of the image (size along the X-axis). - - \c _height defines the number of \a rows of the image (size along the Y-axis). - - \c _depth defines the number of \a slices of the image (size along the Z-axis). - - \c _spectrum defines the number of \a channels of the image (size along the C-axis). - - \c _data defines a \a pointer to the \a pixel \a data (of type \c T). - - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with - another image. - - You can access these fields publicly although it is recommended to use the dedicated functions - width(), height(), depth(), spectrum() and ptr() to do so. - Image dimensions are not limited to a specific range (as long as you got enough available memory). - A value of \e 1 usually means that the corresponding dimension is \a flat. - If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. - Empty images should not contain any pixel data and thus, will not be processed by CImg member functions - (a CImgInstanceException will be thrown instead). - Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). - - \par Image declaration and construction - - Declaring an image can be done by using one of the several available constructors. - Here is a list of the most used: - - - Construct images from arbitrary dimensions: - - <tt>CImg<char> img;</tt> declares an empty image. - - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with - \c unsigned \c char pixel values. - - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients. - - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image - (colors are stored as an image with three channels). - - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image - (with \c double pixel values). - - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image - (with \c float pixels, which is the default value of the template parameter \c T). - - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to - do it, or use the specific constructor taking 5 parameters like this: - <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0. - - - Construct images from filenames: - - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg". - - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the - file "analyze.hdr". - - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a> - to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). - - - Construct images from C-style arrays: - - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer - \c data_buffer (of size 256x256=65536). - - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image - from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). - - The complete list of constructors can be found <a href="#constructors">here</a>. - - \par Most useful functions - - The \c CImg<T> class contains a lot of functions that operates on images. - Some of the most useful are: - - - operator()(): Read or write pixel values. - - display(): displays the image in a new window. - **/ - template<typename T> - struct CImg { - - unsigned int _width, _height, _depth, _spectrum; - bool _is_shared; - T *_data; - - //! Simple iterator type, to loop through each pixel value of an image instance. - /** - \note - - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>. - - You will seldom have to use iterators in %CImg, most classical operations - being achieved (often in a faster way) using methods of \c CImg<T>. - \par Example - \code - CImg<float> img("reference.jpg"); // Load image from file - // Set all pixels to '0', with a CImg iterator. - for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0; - img.fill(0); // Do the same with a built-in method - \endcode - **/ - typedef T* iterator; - - //! Simple const iterator type, to loop through each pixel value of a \c const image instance. - /** - \note - - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*. - - You will seldom have to use iterators in %CImg, most classical operations - being achieved (often in a faster way) using methods of \c CImg<T>. - \par Example - \code - const CImg<float> img("reference.jpg"); // Load image from file - float sum = 0; - // Compute sum of all pixel values, with a CImg iterator. - for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it; - const float sum2 = img.sum(); // Do the same with a built-in method - \endcode - **/ - typedef const T* const_iterator; - - //! Pixel value type. - /** - Refer to the type of the pixel values of an image instance. - \note - - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T. - - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for - compatibility with STL naming conventions. - **/ - typedef T value_type; - - // Define common types related to template type T. - typedef typename cimg::superset<T,bool>::type Tbool; - typedef typename cimg::superset<T,unsigned char>::type Tuchar; - typedef typename cimg::superset<T,char>::type Tchar; - typedef typename cimg::superset<T,unsigned short>::type Tushort; - typedef typename cimg::superset<T,short>::type Tshort; - typedef typename cimg::superset<T,unsigned int>::type Tuint; - typedef typename cimg::superset<T,int>::type Tint; - typedef typename cimg::superset<T,cimg_ulong>::type Tulong; - typedef typename cimg::superset<T,cimg_long>::type Tlong; - typedef typename cimg::superset<T,float>::type Tfloat; - typedef typename cimg::superset<T,double>::type Tdouble; - typedef typename cimg::last<T,bool>::type boolT; - typedef typename cimg::last<T,unsigned char>::type ucharT; - typedef typename cimg::last<T,char>::type charT; - typedef typename cimg::last<T,unsigned short>::type ushortT; - typedef typename cimg::last<T,short>::type shortT; - typedef typename cimg::last<T,unsigned int>::type uintT; - typedef typename cimg::last<T,int>::type intT; - typedef typename cimg::last<T,cimg_ulong>::type ulongT; - typedef typename cimg::last<T,cimg_long>::type longT; - typedef typename cimg::last<T,cimg_uint64>::type uint64T; - typedef typename cimg::last<T,cimg_int64>::type int64T; - typedef typename cimg::last<T,float>::type floatT; - typedef typename cimg::last<T,double>::type doubleT; - - //@} - //--------------------------- - // - //! \name Plugins - //@{ - //--------------------------- -#ifdef cimg_plugin -#include cimg_plugin -#endif -#ifdef cimg_plugin1 -#include cimg_plugin1 -#endif -#ifdef cimg_plugin2 -#include cimg_plugin2 -#endif -#ifdef cimg_plugin3 -#include cimg_plugin3 -#endif -#ifdef cimg_plugin4 -#include cimg_plugin4 -#endif -#ifdef cimg_plugin5 -#include cimg_plugin5 -#endif -#ifdef cimg_plugin6 -#include cimg_plugin6 -#endif -#ifdef cimg_plugin7 -#include cimg_plugin7 -#endif -#ifdef cimg_plugin8 -#include cimg_plugin8 -#endif - - //@} - //--------------------------------------------------------- - // - //! \name Constructors / Destructor / Instance Management - //@{ - //--------------------------------------------------------- - - //! Destroy image. - /** - \note - - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances. - - Destroying an empty or shared image does nothing actually. - \warning - - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image - that shares its buffer with the destroyed instance, in order to avoid further invalid memory access - (to a deallocated buffer). - **/ - ~CImg() { - if (!_is_shared) delete[] _data; - } - - //! Construct empty image. - /** - \note - - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum() - are set to \c 0, as well as its pixel buffer pointer data(). - - An empty image may be re-assigned afterwards, e.g. with the family of - assign(unsigned int,unsigned int,unsigned int,unsigned int) methods, - or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T. - - An empty image is never shared. - \par Example - \code - CImg<float> img1, img2; // Construct two empty images - img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image - img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1' - img2.assign(); // Re-assign 'img2' to be an empty image again - \endcode - **/ - CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {} - - //! Construct image with specified size. - /** - \param size_x Image width(). - \param size_y Image height(). - \param size_z Image depth(). - \param size_c Image spectrum() (number of channels). - \note - - It is able to create only \e non-shared images, and allocates thus a pixel buffer data() - for each constructed image instance. - - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of - an \e empty image. - - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated - (e.g. when requested size is too big for available memory). - \warning - - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. - In order to initialize pixel values during construction (e.g. with \c 0), use constructor - CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead. - \par Example - \code - CImg<float> img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values - CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0' - \endcode - **/ - explicit CImg(const unsigned int size_x, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1): - _is_shared(false) { - size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (siz) { - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), - size_x,size_y,size_z,size_c); - } - } else { _width = _height = _depth = _spectrum = 0; _data = 0; } - } - - //! Construct image with specified size and initialize pixel values. - /** - \param size_x Image width(). - \param size_y Image height(). - \param size_z Image depth(). - \param size_c Image spectrum() (number of channels). - \param value Initialization value. - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), - but it also fills the pixel buffer with the specified \c value. - \warning - - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels - (e.g. RGB vector, for color images). - For this task, you may use fillC() after construction. - **/ - CImg(const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, const T& value): - _is_shared(false) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (siz) { - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), - size_x,size_y,size_z,size_c); - } - fill(value); - } else { _width = _height = _depth = _spectrum = 0; _data = 0; } - } - - //! Construct image with specified size and initialize pixel values from a sequence of integers. - /** - Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, - with pixels of type \c T, and initialize pixel - values from the specified sequence of integers \c value0,\c value1,\c ... - \param size_x Image width(). - \param size_y Image height(). - \param size_z Image depth(). - \param size_c Image spectrum() (number of channels). - \param value0 First value of the initialization sequence (must be an \e integer). - \param value1 Second value of the initialization sequence (must be an \e integer). - \param ... - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills - the pixel buffer with a sequence of specified integer values. - \warning - - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence. - Otherwise, the constructor may crash or fill your image pixels with garbage. - \par Example - \code - const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image - 0,255,0,255, // Set the 4 values for the red component - 0,0,255,255, // Set the 4 values for the green component - 64,64,64,64); // Set the 4 values for the blue component - img.resize(150,150).display(); - \endcode - \image html ref_constructor1.jpg - **/ - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, - const int value0, const int value1, ...): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { -#define _CImg_stdarg(img,a0,a1,N,t) { \ - size_t _siz = (size_t)N; \ - if (_siz--) { \ - va_list ap; \ - va_start(ap,a1); \ - T *ptrd = (img)._data; \ - *(ptrd++) = (T)a0; \ - if (_siz--) { \ - *(ptrd++) = (T)a1; \ - for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ - } \ - va_end(ap); \ - } \ - } - assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int); - } - -#if cimg_use_cpp11==1 - //! Construct image with specified size and initialize pixel values from an initializer list of integers. - /** - Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, - with pixels of type \c T, and initialize pixel - values from the specified initializer list of integers { \c value0,\c value1,\c ... } - \param size_x Image width(). - \param size_y Image height(). - \param size_z Image depth(). - \param size_c Image spectrum() (number of channels). - \param { value0, value1, ... } Initialization list - \param repeat_values Tells if the value filling process is repeated over the image. - - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills - the pixel buffer with a sequence of specified integer values. - \par Example - \code - const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image - { 0,255,0,255, // Set the 4 values for the red component - 0,0,255,255, // Set the 4 values for the green component - 64,64,64,64 }); // Set the 4 values for the blue component - img.resize(150,150).display(); - \endcode - \image html ref_constructor1.jpg - **/ - template<typename t> - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, - const std::initializer_list<t> values, - const bool repeat_values=true): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { -#define _cimg_constructor_cpp11(repeat_values) \ - auto it = values.begin(); \ - size_t siz = size(); \ - if (repeat_values) for (T *ptrd = _data; siz--; ) { \ - *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \ - else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); } - assign(size_x,size_y,size_z,size_c); - _cimg_constructor_cpp11(repeat_values); - } - - template<typename t> - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, - std::initializer_list<t> values, - const bool repeat_values=true): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(size_x,size_y,size_z); - _cimg_constructor_cpp11(repeat_values); - } - - template<typename t> - CImg(const unsigned int size_x, const unsigned int size_y, - std::initializer_list<t> values, - const bool repeat_values=true): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(size_x,size_y); - _cimg_constructor_cpp11(repeat_values); - } - - template<typename t> - CImg(const unsigned int size_x, - std::initializer_list<t> values, - const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(size_x); - _cimg_constructor_cpp11(repeat_values); - } - - //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers. - /** - Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1, - with pixels of type \c T, and initialize pixel - values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is - given by the size of the initializer list. - \param { value0, value1, ... } Initialization list - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1, - but it also fills the pixel buffer with a sequence of specified integer values. - \par Example - \code - const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values - img.resize(150,150).display(); - \endcode - \image html ref_constructor1.jpg - **/ - template<typename t> - CImg(const std::initializer_list<t> values): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(values.size(),1,1,1); - auto it = values.begin(); - unsigned int siz = _width; - for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); - } - - template<typename t> - CImg<T> & operator=(std::initializer_list<t> values) { - _cimg_constructor_cpp11(siz>values.size()); - return *this; - } -#endif - - //! Construct image with specified size and initialize pixel values from a sequence of doubles. - /** - Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, - and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ... - \param size_x Image width(). - \param size_y Image height(). - \param size_z Image depth(). - \param size_c Image spectrum() (number of channels). - \param value0 First value of the initialization sequence (must be a \e double). - \param value1 Second value of the initialization sequence (must be a \e double). - \param ... - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but - takes a sequence of double values instead of integers. - \warning - - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence. - Otherwise, the constructor may crash or fill your image with garbage. - For instance, the code below will probably crash on most platforms: - \code - const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'! - \endcode - **/ - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, - const double value0, const double value1, ...): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double); - } - - //! Construct image with specified size and initialize pixel values from a value string. - /** - Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, - and initializes pixel values from the specified string \c values. - \param size_x Image width(). - \param size_y Image height(). - \param size_z Image depth(). - \param size_c Image spectrum() (number of channels). - \param values Value string describing the way pixel values are set. - \param repeat_values Tells if the value filling process is repeated over the image. - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills - the pixel buffer with values described in the value string \c values. - - Value string \c values may describe two different filling processes: - - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>. - In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence. - - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>. - In this case, parameter \c repeat_values is pointless. - - For both cases, specifying \c repeat_values is mandatory. - It disambiguates the possible overloading of constructor - CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>. - - A \c CImgArgumentException is thrown when an invalid value string \c values is specified. - \par Example - \code - const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence - img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula - (img1,img2).display(); - \endcode - \image html ref_constructor2.jpg - **/ - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, - const char *const values, const bool repeat_values):_is_shared(false) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (siz) { - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), - size_x,size_y,size_z,size_c); - } - fill(values,repeat_values); - } else { _width = _height = _depth = _spectrum = 0; _data = 0; } - } - - //! Construct image with specified size and initialize pixel values from a memory buffer. - /** - Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, - and initializes pixel values from the specified \c t* memory buffer. - \param values Pointer to the input memory buffer. - \param size_x Image width(). - \param size_y Image height(). - \param size_z Image depth(). - \param size_c Image spectrum() (number of channels). - \param is_shared Tells if input memory buffer must be shared by the current instance. - \note - - If \c is_shared is \c false, the image instance allocates its own pixel buffer, - and values from the specified input buffer are copied to the instance buffer. - If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy. - - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its - own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared - image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator. - - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated - (e.g. when requested size is too big for available memory). - \warning - - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data() - (e.g. already deallocated). - \par Example - \code - unsigned char tab[256*256] = { 0 }; - CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab' - img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab' - tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1' - \endcode - **/ - template<typename t> - CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) { - if (is_shared) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgArgumentException(_cimg_instance - "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance " - "from a (%s*) buffer (pixel types are different).", - cimg_instance, - size_x,size_y,size_z,size_c,CImg<t>::pixel_type()); - } - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (values && siz) { - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), - size_x,size_y,size_z,size_c); - - } - const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); - } else { _width = _height = _depth = _spectrum = 0; _data = 0; } - } - - //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. - CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (values && siz) { - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared; - if (_is_shared) _data = const_cast<T*>(values); - else { - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), - size_x,size_y,size_z,size_c); - } - std::memcpy(_data,values,siz*sizeof(T)); - } - } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } - } - - //! Construct image from reading an image file. - /** - Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from - an image file. - \param filename Filename, as a C-string. - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image - dimensions and pixel values from the specified image file. - - The recognition of the image file format by %CImg higly depends on the tools installed on your system - and on the external libraries you used to link your code against. - - Considered pixel type \c T should better fit the file format specification, or data loss may occur during - file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file). - - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not - recognized. - \par Example - \code - const CImg<float> img("reference.jpg"); - img.display(); - \endcode - \image html ref_image.jpg - **/ - explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(filename); - } - - //! Construct image copy. - /** - Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance. - \param img Input image to copy. - \note - - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the - input image \c img. - - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also - \e shared, and shares its pixel buffer with \c img. - Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img. - This behavior is needful to allow functions to return shared images. - - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input - image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and - \c t are different. - - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than - with different types. - - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated - (e.g. not enough available memory). - **/ - template<typename t> - CImg(const CImg<t>& img):_is_shared(false) { - const size_t siz = (size_t)img.size(); - if (img._data && siz) { - _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), - img._width,img._height,img._depth,img._spectrum); - } - const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); - } else { _width = _height = _depth = _spectrum = 0; _data = 0; } - } - - //! Construct image copy \specialization. - CImg(const CImg<T>& img) { - const size_t siz = (size_t)img.size(); - if (img._data && siz) { - _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; - _is_shared = img._is_shared; - if (_is_shared) _data = const_cast<T*>(img._data); - else { - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), - img._width,img._height,img._depth,img._spectrum); - - } - std::memcpy(_data,img._data,siz*sizeof(T)); - } - } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } - } - - //! Advanced copy constructor. - /** - Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance, - while forcing the shared state of the constructed copy. - \param img Input image to copy. - \param is_shared Tells about the shared state of the constructed copy. - \note - - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of - the constructed image, which does not depend anymore on the shared state of the input image \c img: - - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img. - For that case, the pixel types \c T and \c t \e must be the same. - - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input - image \c img is shared or not. - - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t. - **/ - template<typename t> - CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) { - if (is_shared) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgArgumentException(_cimg_instance - "CImg(): Invalid construction request of a shared instance from a " - "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).", - cimg_instance, - CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data); - } - const size_t siz = (size_t)img.size(); - if (img._data && siz) { - _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), - img._width,img._height,img._depth,img._spectrum); - } - const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); - } else { _width = _height = _depth = _spectrum = 0; _data = 0; } - } - - //! Advanced copy constructor \specialization. - CImg(const CImg<T>& img, const bool is_shared) { - const size_t siz = (size_t)img.size(); - if (img._data && siz) { - _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; - _is_shared = is_shared; - if (_is_shared) _data = const_cast<T*>(img._data); - else { - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), - img._width,img._height,img._depth,img._spectrum); - } - std::memcpy(_data,img._data,siz*sizeof(T)); - } - } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } - } - - //! Construct image with dimensions borrowed from another image. - /** - Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing - \c CImg<t> instance. - \param img Input image from which dimensions are borrowed. - \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions. - \note - - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions - (\e not its pixel values) from an existing \c CImg<t> instance. - - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. - In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T) - instead. - \par Example - \code - const CImg<float> img1(256,128,1,3), // 'img1' is a 256x128x1x3 image - img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image - img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image - img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0') - \endcode - **/ - template<typename t> - CImg(const CImg<t>& img, const char *const dimensions): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(img,dimensions); - } - - //! Construct image with dimensions borrowed from another image and initialize pixel values. - /** - Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing - \c CImg<t> instance, and set all pixel values to specified \c value. - \param img Input image from which dimensions are borrowed. - \param dimensions String describing the image size along the X,Y,Z and V-dimensions. - \param value Value used for initialization. - \note - - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value. - **/ - template<typename t> - CImg(const CImg<t>& img, const char *const dimensions, const T& value): - _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - assign(img,dimensions).fill(value); - } - - //! Construct image from a display window. - /** - Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance. - \param disp Input display window. - \note - - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay. - - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3 - (i.e. a 2D color image). - - The image pixels are read as 8-bits RGB values. - **/ - explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - disp.snapshot(*this); - } - - // Constructor and assignment operator for rvalue references (c++11). - // This avoids an additional image copy for methods returning new images. Can save RAM for big images ! -#if cimg_use_cpp11==1 - CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { - swap(img); - } - CImg<T>& operator=(CImg<T>&& img) { - if (_is_shared) return assign(img); - return img.swap(*this); - } -#endif - - //! Construct empty image \inplace. - /** - In-place version of the default constructor CImg(). It simply resets the instance to an empty image. - **/ - CImg<T>& assign() { - if (!_is_shared) delete[] _data; - _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; - return *this; - } - - //! Construct image with specified size \inplace. - /** - In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int). - **/ - CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (!siz) return assign(); - const size_t curr_siz = (size_t)size(); - if (siz!=curr_siz) { - if (_is_shared) - throw CImgArgumentException(_cimg_instance - "assign(): Invalid assignement request of shared instance from specified " - "image (%u,%u,%u,%u).", - cimg_instance, - size_x,size_y,size_z,size_c); - else { - delete[] _data; - try { _data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), - size_x,size_y,size_z,size_c); - } - } - } - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; - return *this; - } - - //! Construct image with specified size and initialize pixel values \inplace. - /** - In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T). - **/ - CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, const T& value) { - return assign(size_x,size_y,size_z,size_c).fill(value); - } - - //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace. - /** - In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...). - **/ - CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, - const int value0, const int value1, ...) { - assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int); - return *this; - } - - //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace. - /** - In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...). - **/ - CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, - const double value0, const double value1, ...) { - assign(size_x,size_y,size_z,size_c); - _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double); - return *this; - } - - //! Construct image with specified size and initialize pixel values from a value string \inplace. - /** - In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool). - **/ - CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, - const char *const values, const bool repeat_values) { - return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values); - } - - //! Construct image with specified size and initialize pixel values from a memory buffer \inplace. - /** - In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int). - **/ - template<typename t> - CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (!values || !siz) return assign(); - assign(size_x,size_y,size_z,size_c); - const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); - return *this; - } - - //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. - CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (!values || !siz) return assign(); - const size_t curr_siz = (size_t)size(); - if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c); - if (_is_shared || values + siz<_data || values>=_data + size()) { - assign(size_x,size_y,size_z,size_c); - if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T)); - else std::memcpy((void*)_data,(void*)values,siz*sizeof(T)); - } else { - T *new_data = 0; - try { new_data = new T[siz]; } catch (...) { - _width = _height = _depth = _spectrum = 0; _data = 0; - throw CImgInstanceException(_cimg_instance - "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", - cimg_instance, - cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), - size_x,size_y,size_z,size_c); - } - std::memcpy((void*)new_data,(void*)values,siz*sizeof(T)); - delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; - } - return *this; - } - - //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. - template<typename t> - CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, const bool is_shared) { - if (is_shared) - throw CImgArgumentException(_cimg_instance - "assign(): Invalid assignment request of shared instance from (%s*) buffer" - "(pixel types are different).", - cimg_instance, - CImg<t>::pixel_type()); - return assign(values,size_x,size_y,size_z,size_c); - } - - //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. - CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, const bool is_shared) { - const size_t siz = (size_t)size_x*size_y*size_z*size_c; - if (!values || !siz) return assign(); - if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); } - else { - if (!_is_shared) { - if (values + siz<_data || values>=_data + size()) assign(); - else cimg::warn(_cimg_instance - "assign(): Shared image instance has overlapping memory.", - cimg_instance); - } - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; - _data = const_cast<T*>(values); - } - return *this; - } - - //! Construct image from reading an image file \inplace. - /** - In-place version of the constructor CImg(const char*). - **/ - CImg<T>& assign(const char *const filename) { - return load(filename); - } - - //! Construct image copy \inplace. - /** - In-place version of the constructor CImg(const CImg<t>&). - **/ - template<typename t> - CImg<T>& assign(const CImg<t>& img) { - return assign(img._data,img._width,img._height,img._depth,img._spectrum); - } - - //! In-place version of the advanced copy constructor. - /** - In-place version of the constructor CImg(const CImg<t>&,bool). - **/ - template<typename t> - CImg<T>& assign(const CImg<t>& img, const bool is_shared) { - return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared); - } - - //! Construct image with dimensions borrowed from another image \inplace. - /** - In-place version of the constructor CImg(const CImg<t>&,const char*). - **/ - template<typename t> - CImg<T>& assign(const CImg<t>& img, const char *const dimensions) { - if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum); - unsigned int siz[4] = { 0,1,1,1 }, k = 0; - CImg<charT> item(256); - for (const char *s = dimensions; *s && k<4; ++k) { - if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item); - if (*s) { - unsigned int val = 0; char sep = 0; - if (cimg_sscanf(s,"%u%c",&val,&sep)>0) { - if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100; - else siz[k] = val; - while (*s>='0' && *s<='9') ++s; - if (sep=='%') ++s; - } else switch (cimg::lowercase(*s)) { - case 'x' : case 'w' : siz[k] = img._width; ++s; break; - case 'y' : case 'h' : siz[k] = img._height; ++s; break; - case 'z' : case 'd' : siz[k] = img._depth; ++s; break; - case 'c' : case 's' : siz[k] = img._spectrum; ++s; break; - default : - throw CImgArgumentException(_cimg_instance - "assign(): Invalid character '%c' detected in specified dimension string '%s'.", - cimg_instance, - *s,dimensions); - } - } - } - return assign(siz[0],siz[1],siz[2],siz[3]); - } - - //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace. - /** - In-place version of the constructor CImg(const CImg<t>&,const char*,T). - **/ - template<typename t> - CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) { - return assign(img,dimensions).fill(value); - } - - //! Construct image from a display window \inplace. - /** - In-place version of the constructor CImg(const CImgDisplay&). - **/ - CImg<T>& assign(const CImgDisplay &disp) { - disp.snapshot(*this); - return *this; - } - - //! Construct empty image \inplace. - /** - Equivalent to assign(). - \note - - It has been defined for compatibility with STL naming conventions. - **/ - CImg<T>& clear() { - return assign(); - } - - //! Transfer content of an image instance into another one. - /** - Transfer the dimensions and the pixel buffer content of an image instance into another one, - and replace instance by an empty image. It avoids the copy of the pixel buffer - when possible. - \param img Destination image. - \note - - Pixel types \c T and \c t of source and destination images can be different, though the process is - designed to be instantaneous when \c T and \c t are the same. - \par Example - \code - CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0' - dest(16,16); // Construct a 16x16x1x1 (scalar) image - src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image - \endcode - **/ - template<typename t> - CImg<t>& move_to(CImg<t>& img) { - img.assign(*this); - assign(); - return img; - } - - //! Transfer content of an image instance into another one \specialization. - CImg<T>& move_to(CImg<T>& img) { - if (_is_shared || img._is_shared) img.assign(*this); - else swap(img); - assign(); - return img; - } - - //! Transfer content of an image instance into a new image in an image list. - /** - Transfer the dimensions and the pixel buffer content of an image instance - into a newly inserted image at position \c pos in specified \c CImgList<t> instance. - \param list Destination list. - \param pos Position of the newly inserted image in the list. - \note - - When optional parameter \c pos is ommited, the image instance is transfered as a new - image at the end of the specified \c list. - - It is convenient to sequentially insert new images into image lists, with no - additional copies of memory buffer. - \par Example - \code - CImgList<float> list; // Construct an empty image list - CImg<float> img("reference.jpg"); // Read image from filename - img.move_to(list); // Transfer image content as a new item in the list (no buffer copy) - \endcode - **/ - template<typename t> - CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) { - const unsigned int npos = pos>list._width?list._width:pos; - move_to(list.insert(1,npos)[npos]); - return list; - } - - //! Swap fields of two image instances. - /** - \param img Image to swap fields with. - \note - - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing - with algorithms requiring two swapping buffers. - \par Example - \code - CImg<float> img1("lena.jpg"), - img2("milla.jpg"); - img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena' - \endcode - **/ - CImg<T>& swap(CImg<T>& img) { - cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum); - cimg::swap(_data,img._data); - cimg::swap(_is_shared,img._is_shared); - return img; - } - - //! Return a reference to an empty image. - /** - \note - This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes, - e.g. - \code - void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty()); - \endcode - **/ - static CImg<T>& empty() { - static CImg<T> _empty; - return _empty.assign(); - } - - //! Return a reference to an empty image \const. - static const CImg<T>& const_empty() { - static const CImg<T> _empty; - return _empty; - } - - //@} - //------------------------------------------ - // - //! \name Overloaded Operators - //@{ - //------------------------------------------ - - //! Access to a pixel value. - /** - Return a reference to a located pixel value of the image instance, - being possibly \e const, whether the image instance is \e const or not. - This is the standard method to get/set pixel values in \c CImg<T> images. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note - - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to - <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>. - - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the - corresponding dimension is equal to \c 1. - For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of - <tt>img(x,y,0,c)</tt>. - \warning - - There is \e no boundary checking done in this operator, to make it as fast as possible. - You \e must take care of out-of-bounds access by yourself, if necessary. - For debuging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary - checking operations in this operator. In that case, warning messages will be printed on the error output - when accessing out-of-bounds pixels. - \par Example - \code - CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0' - const float - valR = img(10,10,0,0), // Read red value at coordinates (10,10) - valG = img(10,10,0,1), // Read green value at coordinates (10,10) - valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted) - avg = (valR + valG + valB)/3; // Compute average pixel value - img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value - \endcode - **/ -#if cimg_verbosity>=3 - T& operator()(const unsigned int x, const unsigned int y=0, - const unsigned int z=0, const unsigned int c=0) { - const ulongT off = (ulongT)offset(x,y,z,c); - if (!_data || off>=size()) { - cimg::warn(_cimg_instance - "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].", - cimg_instance, - (int)x,(int)y,(int)z,(int)c,off); - return *_data; - } - else return _data[off]; - } - - //! Access to a pixel value \const. - const T& operator()(const unsigned int x, const unsigned int y=0, - const unsigned int z=0, const unsigned int c=0) const { - return const_cast<CImg<T>*>(this)->operator()(x,y,z,c); - } - - //! Access to a pixel value. - /** - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>. - \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>. - \note - - Similar to (but faster than) operator()(). - It uses precomputed offsets to optimize memory access. You may use it to optimize - the reading/writing of several pixel values in the same image (e.g. in a loop). - **/ - T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, - const ulongT wh, const ulongT whd=0) { - cimg::unused(wh,whd); - return (*this)(x,y,z,c); - } - - //! Access to a pixel value \const. - const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, - const ulongT wh, const ulongT whd=0) const { - cimg::unused(wh,whd); - return (*this)(x,y,z,c); - } -#else - T& operator()(const unsigned int x) { - return _data[x]; - } - - const T& operator()(const unsigned int x) const { - return _data[x]; - } - - T& operator()(const unsigned int x, const unsigned int y) { - return _data[x + y*_width]; - } - - const T& operator()(const unsigned int x, const unsigned int y) const { - return _data[x + y*_width]; - } - - T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) { - return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; - } - - const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const { - return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; - } - - T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) { - return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; - } - - const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const { - return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; - } - - T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, - const ulongT wh) { - return _data[x + y*_width + z*wh]; - } - - const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, - const ulongT wh) const { - return _data[x + y*_width + z*wh]; - } - - T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, - const ulongT wh, const ulongT whd) { - return _data[x + y*_width + z*wh + c*whd]; - } - - const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, - const ulongT wh, const ulongT whd) const { - return _data[x + y*_width + z*wh + c*whd]; - } -#endif - - //! Implicitely cast an image into a \c T*. - /** - Implicitely cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance - is \e const or not. The returned pointer points on the first value of the image pixel buffer. - \note - - It simply returns the pointer data() to the pixel buffer. - - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g. - \code - CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image - if (img1) { // Test succeeds, 'img1' is not an empty image - if (!img2) { // Test succeeds, 'img2' is an empty image - std::printf("'img1' is not empty, 'img2' is empty."); - } - } - \endcode - - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g. - \code - CImg<float> img(100,100); - const float value = img[99]; // Access to value of the last pixel on the first row - img[510] = 255; // Set pixel value at (10,5) - \endcode - **/ - operator T*() { - return _data; - } - - //! Implicitely cast an image into a \c T* \const. - operator const T*() const { - return _data; - } - - //! Assign a value to all image pixels. - /** - Assign specified \c value to each pixel value of the image instance. - \param value Value that will be assigned to image pixels. - \note - - The image size is never modified. - - The \c value may be casted to pixel type \c T if necessary. - \par Example - \code - CImg<char> img(100,100); // Declare image (with garbage values) - img = 0; // Set all pixel values to '0' - img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char') - \endcode - **/ - CImg<T>& operator=(const T& value) { - return fill(value); - } - - //! Assign pixels values from a specified expression. - /** - Initialize all pixel values from the specified string \c expression. - \param expression Value string describing the way pixel values are set. - \note - - String parameter \c expression may describe different things: - - If \c expression is a list of values (as in \c "1,2,3,8,3,2"), or a formula (as in \c "(x*y)%255"), - the pixel values are set from specified \c expression and the image size is not modified. - - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and - replace the image instance. The image size is modified if necessary. - \par Example - \code - CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with unitialized values - img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence - img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula - img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified) - (img1,img2,img3).display(); - \endcode - \image html ref_operator_eq.jpg - **/ - CImg<T>& operator=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - _fill(expression,true,1,0,0,"operator=",0); - } catch (CImgException&) { - cimg::exception_mode(omode); - load(expression); - } - cimg::exception_mode(omode); - return *this; - } - - //! Copy an image into the current image instance. - /** - Similar to the in-place copy constructor assign(const CImg<t>&). - **/ - template<typename t> - CImg<T>& operator=(const CImg<t>& img) { - return assign(img); - } - - //! Copy an image into the current image instance \specialization. - CImg<T>& operator=(const CImg<T>& img) { - return assign(img); - } - - //! Copy the content of a display window to the current image instance. - /** - Similar to assign(const CImgDisplay&). - **/ - CImg<T>& operator=(const CImgDisplay& disp) { - disp.snapshot(*this); - return *this; - } - - //! In-place addition operator. - /** - Add specified \c value to all pixels of an image instance. - \param value Value to add. - \note - - Resulting pixel values are casted to fit the pixel type \c T. - For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed. - - Overflow values are treated as with standard C++ numeric types. For instance, - \code - CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255' - img+=1; // Add '1' to each pixels -> Overflow - // here all pixels of image 'img' are equal to '0'. - \endcode - - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double, - and use cut() after addition. - \par Example - \code - CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]) - CImg<float> img2(img1); // Construct a float-valued copy of 'img1' - img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats - img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint - img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1' - const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way - (img1,img2,img3).display(); - \endcode - \image html ref_operator_plus.jpg - **/ - template<typename t> - CImg<T>& operator+=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,*ptr + value,524288); - return *this; - } - - //! In-place addition operator. - /** - Add values to image pixels, according to the specified string \c expression. - \param expression Value string describing the way pixel values are added. - \note - - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance, - instead of assigning them. - **/ - CImg<T>& operator+=(const char *const expression) { - return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this); - } - - //! In-place addition operator. - /** - Add values to image pixels, according to the values of the input image \c img. - \param img Input image to add. - \note - - The size of the image instance is never modified. - - It is not mandatory that input image \c img has the same size as the image instance. - If less values are available in \c img, then the values are added periodically. For instance, adding one - WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3) - means each color channel will be incremented with the same values at the same locations. - \par Example - \code - CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3) - // Construct a scalar shading (img2.spectrum()==1). - const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); - img1+=img2; // Add shading to each channel of 'img1' - img1.cut(0,255); // Prevent [0,255] overflow - (img2,img1).display(); - \endcode - \image html ref_operator_plus1.jpg - **/ - template<typename t> - CImg<T>& operator+=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this+=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)(*ptrd + *(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++)); - } - return *this; - } - - //! In-place increment operator (prefix). - /** - Add \c 1 to all image pixels, and return a reference to the current incremented image instance. - \note - - Writing \c ++img is equivalent to \c img+=1. - **/ - CImg<T>& operator++() { - if (is_empty()) return *this; - cimg_openmp_for(*this,*ptr + 1,524288); - return *this; - } - - //! In-place increment operator (postfix). - /** - Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance. - \note - - Use the prefixed version operator++() if you don't need a copy of the initial - (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage. - **/ - CImg<T> operator++(int) { - const CImg<T> copy(*this,false); - ++*this; - return copy; - } - - //! Return a non-shared copy of the image instance. - /** - \note - - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T. - Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image, - and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no - information about the shared state of the input image. - - Writing \c (+img) is equivalent to \c CImg<T>(img,false). - **/ - CImg<T> operator+() const { - return CImg<T>(*this,false); - } - - //! Addition operator. - /** - Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator+(const t value) const { - return CImg<_cimg_Tt>(*this,false)+=value; - } - - //! Addition operator. - /** - Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - CImg<Tfloat> operator+(const char *const expression) const { - return CImg<Tfloat>(*this,false)+=expression; - } - - //! Addition operator. - /** - Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator+(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this,false)+=img; - } - - //! In-place substraction operator. - /** - Similar to operator+=(const t), except that it performs a substraction instead of an addition. - **/ - template<typename t> - CImg<T>& operator-=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,*ptr - value,524288); - return *this; - } - - //! In-place substraction operator. - /** - Similar to operator+=(const char*), except that it performs a substraction instead of an addition. - **/ - CImg<T>& operator-=(const char *const expression) { - return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this); - } - - //! In-place substraction operator. - /** - Similar to operator+=(const CImg<t>&), except that it performs a substraction instead of an addition. - **/ - template<typename t> - CImg<T>& operator-=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this-=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)(*ptrd - *(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++)); - } - return *this; - } - - //! In-place decrement operator (prefix). - /** - Similar to operator++(), except that it performs a decrement instead of an increment. - **/ - CImg<T>& operator--() { - if (is_empty()) return *this; - cimg_openmp_for(*this,*ptr - 1,524288); - return *this; - } - - //! In-place decrement operator (postfix). - /** - Similar to operator++(int), except that it performs a decrement instead of an increment. - **/ - CImg<T> operator--(int) { - const CImg<T> copy(*this,false); - --*this; - return copy; - } - - //! Replace each pixel by its opposite value. - /** - \note - - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types. - For instance, the \c unsigned \c char opposite of \c 1 is \c 255. - \par Example - \code - const CImg<unsigned char> - img1("reference.jpg"), // Load a RGB color image - img2 = -img1; // Compute its opposite (in 'unsigned char') - (img1,img2).display(); - \endcode - \image html ref_operator_minus.jpg - **/ - CImg<T> operator-() const { - return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this; - } - - //! Substraction operator. - /** - Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator-(const t value) const { - return CImg<_cimg_Tt>(*this,false)-=value; - } - - //! Substraction operator. - /** - Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - CImg<Tfloat> operator-(const char *const expression) const { - return CImg<Tfloat>(*this,false)-=expression; - } - - //! Substraction operator. - /** - Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator-(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this,false)-=img; - } - - //! In-place multiplication operator. - /** - Similar to operator+=(const t), except that it performs a multiplication instead of an addition. - **/ - template<typename t> - CImg<T>& operator*=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,*ptr * value,262144); - return *this; - } - - //! In-place multiplication operator. - /** - Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. - **/ - CImg<T>& operator*=(const char *const expression) { - return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this)); - } - - //! In-place multiplication operator. - /** - Replace the image instance by the matrix multiplication between the image instance and the specified matrix - \c img. - \param img Second operand of the matrix multiplication. - \note - - It does \e not compute a pointwise multiplication between two images. For this purpose, use - mul(const CImg<t>&) instead. - - The size of the image instance can be modified by this operator. - \par Example - \code - CImg<float> A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4] - const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2] - A*=X; // Assign matrix multiplication A*X to 'A' - // 'A' is now a 1x2 vector whose values are [5;11]. - \endcode - **/ - template<typename t> - CImg<T>& operator*=(const CImg<t>& img) { - return ((*this)*img).move_to(*this); - } - - //! Multiplication operator. - /** - Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator*(const t value) const { - return CImg<_cimg_Tt>(*this,false)*=value; - } - - //! Multiplication operator. - /** - Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - CImg<Tfloat> operator*(const char *const expression) const { - return CImg<Tfloat>(*this,false)*=expression; - } - - //! Multiplication operator. - /** - Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator*(const CImg<t>& img) const { - typedef _cimg_Ttdouble Ttdouble; - typedef _cimg_Tt Tt; - if (_width!=img._height || _depth!=1 || _spectrum!=1) - throw CImgArgumentException(_cimg_instance - "operator*(): Invalid multiplication of instance by specified " - "matrix (%u,%u,%u,%u,%p)", - cimg_instance, - img._width,img._height,img._depth,img._spectrum,img._data); - CImg<Tt> res(img._width,_height); - - // Check for common cases to optimize. - if (img._width==1) { // Matrix * Vector - if (_height==1) switch (_width) { // Vector^T * Vector - case 1 : - res[0] = (Tt)((Ttdouble)_data[0]*img[0]); - return res; - case 2 : - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); - return res; - case 3 : - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + - (Ttdouble)_data[2]*img[2]); - return res; - case 4 : - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + - (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); - return res; - default : { - Ttdouble val = 0; - cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i]; - res[0] = val; - return res; - } - } else if (_height==_width) switch (_width) { // Square_matrix * Vector - case 2 : // 2x2_matrix * Vector - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); - res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]); - return res; - case 3 : // 3x3_matrix * Vector - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + - (Ttdouble)_data[2]*img[2]); - res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] + - (Ttdouble)_data[5]*img[2]); - res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] + - (Ttdouble)_data[8]*img[2]); - return res; - case 4 : // 4x4_matrix * Vector - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + - (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); - res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] + - (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]); - res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] + - (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]); - res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] + - (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]); - return res; - } - } else if (_height==_width) { - if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix - case 2 : // 2x2_matrix * 2x2_matrix - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]); - res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]); - res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]); - res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]); - return res; - case 3 : // 3x3_matrix * 3x3_matrix - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] + - (Ttdouble)_data[2]*img[6]); - res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] + - (Ttdouble)_data[2]*img[7]); - res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] + - (Ttdouble)_data[2]*img[8]); - res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] + - (Ttdouble)_data[5]*img[6]); - res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] + - (Ttdouble)_data[5]*img[7]); - res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] + - (Ttdouble)_data[5]*img[8]); - res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] + - (Ttdouble)_data[8]*img[6]); - res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] + - (Ttdouble)_data[8]*img[7]); - res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] + - (Ttdouble)_data[8]*img[8]); - return res; - case 4 : // 4x4_matrix * 4x4_matrix - res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] + - (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]); - res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] + - (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]); - res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] + - (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]); - res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] + - (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]); - res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] + - (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]); - res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] + - (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]); - res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] + - (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]); - res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] + - (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]); - res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] + - (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]); - res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] + - (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]); - res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] + - (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]); - res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] + - (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]); - res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] + - (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]); - res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] + - (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]); - res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] + - (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]); - res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] + - (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]); - return res; - } else switch (_width) { // Square_matrix * Matrix - case 2 : { // 2x2_matrix * Matrix - const t *ps0 = img.data(), *ps1 = img.data(0,1); - Tt *pd0 = res.data(), *pd1 = res.data(0,1); - const Ttdouble - a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], - a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3]; - cimg_forX(img,i) { - const Ttdouble x = (Ttdouble)*(ps0++), y = (Ttdouble)*(ps1++); - *(pd0++) = (Tt)(a0*x + a1*y); - *(pd1++) = (Tt)(a2*x + a3*y); - } - return res; - } - case 3 : { // 3x3_matrix * Matrix - const t *ps0 = img.data(), *ps1 = img.data(0,1), *ps2 = img.data(0,2); - Tt *pd0 = res.data(), *pd1 = res.data(0,1), *pd2 = res.data(0,2); - const Ttdouble - a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], - a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], - a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8]; - cimg_forX(img,i) { - const Ttdouble x = (Ttdouble)*(ps0++), y = (Ttdouble)*(ps1++), z = (Ttdouble)*(ps2++); - *(pd0++) = (Tt)(a0*x + a1*y + a2*z); - *(pd1++) = (Tt)(a3*x + a4*y + a5*z); - *(pd2++) = (Tt)(a6*x + a7*y + a8*z); - } - return res; - } - case 4 : { // 4x4_matrix * Matrix - const t *ps0 = img.data(), *ps1 = img.data(0,1), *ps2 = img.data(0,2), *ps3 = img.data(0,3); - Tt *pd0 = res.data(), *pd1 = res.data(0,1), *pd2 = res.data(0,2), *pd3 = res.data(0,3); - const Ttdouble - a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3], - a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], - a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11], - a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14], - a15 = (Ttdouble)_data[15]; - cimg_forX(img,col) { - const Ttdouble x = (Ttdouble)*(ps0++), y = (Ttdouble)*(ps1++), z = (Ttdouble)*(ps2++), - c = (Ttdouble)*(ps3++); - *(pd0++) = (Tt)(a0*x + a1*y + a2*z + a3*c); - *(pd1++) = (Tt)(a4*x + a5*y + a6*z + a7*c); - *(pd2++) = (Tt)(a8*x + a9*y + a10*z + a11*c); - *(pd3++) = (Tt)(a12*x + a13*y + a14*z + a15*c); - } - return res; - } - } - } - - // Fallback to generic version. -#ifdef cimg_use_openmp - cimg_pragma_openmp(parallel for collapse(2) - cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 && - img.size()>(cimg_openmp_sizefactor)*1024)) - cimg_forXY(res,i,j) { - Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (Tt)value; - } -#else - Tt *ptrd = res._data; - cimg_forXY(res,i,j) { - Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (Tt)value; - } -#endif - return res; - } - - //! In-place division operator. - /** - Similar to operator+=(const t), except that it performs a division instead of an addition. - **/ - template<typename t> - CImg<T>& operator/=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,*ptr / value,32768); - return *this; - } - - //! In-place division operator. - /** - Similar to operator+=(const char*), except that it performs a division instead of an addition. - **/ - CImg<T>& operator/=(const char *const expression) { - return div((+*this)._fill(expression,true,1,0,0,"operator/=",this)); - } - - //! In-place division operator. - /** - Replace the image instance by the (right) matrix division between the image instance and the specified - matrix \c img. - \param img Second operand of the matrix division. - \note - - It does \e not compute a pointwise division between two images. For this purpose, use - div(const CImg<t>&) instead. - - It returns the matrix operation \c A*inverse(img). - - The size of the image instance can be modified by this operator. - **/ - template<typename t> - CImg<T>& operator/=(const CImg<t>& img) { - return (*this*img.get_invert()).move_to(*this); - } - - //! Division operator. - /** - Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator/(const t value) const { - return CImg<_cimg_Tt>(*this,false)/=value; - } - - //! Division operator. - /** - Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - CImg<Tfloat> operator/(const char *const expression) const { - return CImg<Tfloat>(*this,false)/=expression; - } - - //! Division operator. - /** - Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator/(const CImg<t>& img) const { - return (*this)*img.get_invert(); - } - - //! In-place modulo operator. - /** - Similar to operator+=(const t), except that it performs a modulo operation instead of an addition. - **/ - template<typename t> - CImg<T>& operator%=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384); - return *this; - } - - //! In-place modulo operator. - /** - Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. - **/ - CImg<T>& operator%=(const char *const expression) { - return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this); - } - - //! In-place modulo operator. - /** - Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition. - **/ - template<typename t> - CImg<T>& operator%=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this%=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = cimg::mod(*ptrd,(T)*(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++)); - } - return *this; - } - - //! Modulo operator. - /** - Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator%(const t value) const { - return CImg<_cimg_Tt>(*this,false)%=value; - } - - //! Modulo operator. - /** - Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - CImg<Tfloat> operator%(const char *const expression) const { - return CImg<Tfloat>(*this,false)%=expression; - } - - //! Modulo operator. - /** - Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. - **/ - template<typename t> - CImg<_cimg_Tt> operator%(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this,false)%=img; - } - - //! In-place bitwise AND operator. - /** - Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition. - **/ - template<typename t> - CImg<T>& operator&=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,(ulongT)*ptr & (ulongT)value,32768); - return *this; - } - - //! In-place bitwise AND operator. - /** - Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. - **/ - CImg<T>& operator&=(const char *const expression) { - return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this); - } - - //! In-place bitwise AND operator. - /** - Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition. - **/ - template<typename t> - CImg<T>& operator&=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this&=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++)); - } - return *this; - } - - //! Bitwise AND operator. - /** - Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator&(const t value) const { - return (+*this)&=value; - } - - //! Bitwise AND operator. - /** - Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - CImg<T> operator&(const char *const expression) const { - return (+*this)&=expression; - } - - //! Bitwise AND operator. - /** - Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator&(const CImg<t>& img) const { - return (+*this)&=img; - } - - //! In-place bitwise OR operator. - /** - Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition. - **/ - template<typename t> - CImg<T>& operator|=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,(ulongT)*ptr | (ulongT)value,32768); - return *this; - } - - //! In-place bitwise OR operator. - /** - Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. - **/ - CImg<T>& operator|=(const char *const expression) { - return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this); - } - - //! In-place bitwise OR operator. - /** - Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition. - **/ - template<typename t> - CImg<T>& operator|=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this|=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++)); - } - return *this; - } - - //! Bitwise OR operator. - /** - Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator|(const t value) const { - return (+*this)|=value; - } - - //! Bitwise OR operator. - /** - Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - CImg<T> operator|(const char *const expression) const { - return (+*this)|=expression; - } - - //! Bitwise OR operator. - /** - Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator|(const CImg<t>& img) const { - return (+*this)|=img; - } - - //! In-place bitwise XOR operator. - /** - Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition. - \warning - - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead. - **/ - template<typename t> - CImg<T>& operator^=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,(ulongT)*ptr ^ (ulongT)value,32768); - return *this; - } - - //! In-place bitwise XOR operator. - /** - Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition. - \warning - - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. - **/ - CImg<T>& operator^=(const char *const expression) { - return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this); - } - - //! In-place bitwise XOR operator. - /** - Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition. - \warning - - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead. - **/ - template<typename t> - CImg<T>& operator^=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this^=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++)); - } - return *this; - } - - //! Bitwise XOR operator. - /** - Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator^(const t value) const { - return (+*this)^=value; - } - - //! Bitwise XOR operator. - /** - Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - CImg<T> operator^(const char *const expression) const { - return (+*this)^=expression; - } - - //! Bitwise XOR operator. - /** - Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator^(const CImg<t>& img) const { - return (+*this)^=img; - } - - //! In-place bitwise left shift operator. - /** - Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition. - **/ - template<typename t> - CImg<T>& operator<<=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536); - return *this; - } - - //! In-place bitwise left shift operator. - /** - Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. - **/ - CImg<T>& operator<<=(const char *const expression) { - return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this); - } - - //! In-place bitwise left shift operator. - /** - Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition. - **/ - template<typename t> - CImg<T>& operator<<=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this^=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)((longT)*ptrd << (int)*(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++)); - } - return *this; - } - - //! Bitwise left shift operator. - /** - Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator<<(const t value) const { - return (+*this)<<=value; - } - - //! Bitwise left shift operator. - /** - Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - CImg<T> operator<<(const char *const expression) const { - return (+*this)<<=expression; - } - - //! Bitwise left shift operator. - /** - Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of - operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator<<(const CImg<t>& img) const { - return (+*this)<<=img; - } - - //! In-place bitwise right shift operator. - /** - Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition. - **/ - template<typename t> - CImg<T>& operator>>=(const t value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536); - return *this; - } - - //! In-place bitwise right shift operator. - /** - Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. - **/ - CImg<T>& operator>>=(const char *const expression) { - return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this); - } - - //! In-place bitwise right shift operator. - /** - Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition. - **/ - template<typename t> - CImg<T>& operator>>=(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return *this^=+img; - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++)); - } - return *this; - } - - //! Bitwise right shift operator. - /** - Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator>>(const t value) const { - return (+*this)>>=value; - } - - //! Bitwise right shift operator. - /** - Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place. - The pixel type of the returned image is \c T. - **/ - CImg<T> operator>>(const char *const expression) const { - return (+*this)>>=expression; - } - - //! Bitwise right shift operator. - /** - Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of - operating in-place. - The pixel type of the returned image is \c T. - **/ - template<typename t> - CImg<T> operator>>(const CImg<t>& img) const { - return (+*this)>>=img; - } - - //! Bitwise inversion operator. - /** - Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value. - **/ - CImg<T> operator~() const { - CImg<T> res(_width,_height,_depth,_spectrum); - const T *ptrs = _data; - cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; } - return res; - } - - //! Test if all pixels of an image have the same value. - /** - Return \c true is all pixels of the image instance are equal to the specified \c value. - \param value Reference value to compare with. - **/ - template<typename t> - bool operator==(const t value) const { - if (is_empty()) return false; - typedef _cimg_Tt Tt; - bool is_equal = true; - for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {} - return is_equal; - } - - //! Test if all pixel values of an image follow a specified expression. - /** - Return \c true is all pixels of the image instance are equal to the specified \c expression. - \param expression Value string describing the way pixel values are compared. - **/ - bool operator==(const char *const expression) const { - return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this); - } - - //! Test if two images have the same size and values. - /** - Return \c true if the image instance and the input image \c img have the same dimensions and pixel values, - and \c false otherwise. - \param img Input image to compare with. - \note - - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==() - to return \c true. - Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different - pixel types \c T and \c t. - \par Example - \code - const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values) - const CImg<char> img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values) - if (img1==img2) { // Test succeeds, image dimensions and values are the same - std::printf("'img1' and 'img2' have same dimensions and values."); - } - \endcode - **/ - template<typename t> - bool operator==(const CImg<t>& img) const { - typedef _cimg_Tt Tt; - const ulongT siz = size(); - bool is_equal = true; - if (siz!=img.size()) return false; - t *ptrs = img._data + siz; - for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {} - return is_equal; - } - - //! Test if pixels of an image are all different from a value. - /** - Return \c true is all pixels of the image instance are different than the specified \c value. - \param value Reference value to compare with. - **/ - template<typename t> - bool operator!=(const t value) const { - return !((*this)==value); - } - - //! Test if all pixel values of an image are different from a specified expression. - /** - Return \c true is all pixels of the image instance are different to the specified \c expression. - \param expression Value string describing the way pixel values are compared. - **/ - bool operator!=(const char *const expression) const { - return !((*this)==expression); - } - - //! Test if two images have different sizes or values. - /** - Return \c true if the image instance and the input image \c img have different dimensions or pixel values, - and \c false otherwise. - \param img Input image to compare with. - \note - - Writing \c img1!=img2 is equivalent to \c !(img1==img2). - **/ - template<typename t> - bool operator!=(const CImg<t>& img) const { - return !((*this)==img); - } - - //! Construct an image list from two images. - /** - Return a new list of image (\c CImgList instance) containing exactly two elements: - - A copy of the image instance, at position [\c 0]. - - A copy of the specified image \c img, at position [\c 1]. - - \param img Input image that will be the second image of the resulting list. - \note - - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow - in practice (see warning below). - - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are - inserted as new non-shared copies in the resulting list. - - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary. - \warning - - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list. - This may become very expensive in terms of speed and used memory. You should avoid using this technique to - build a new CImgList instance from several images, if you are seeking for performance. - Fast insertions of images in an image list are possible with - CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int). - \par Example - \code - const CImg<float> - img1("reference.jpg"), - img2 = img1.get_mirror('x'), - img3 = img2.get_blur(5); - const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2' - (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3' - \endcode - \image html ref_operator_comma.jpg - **/ - template<typename t> - CImgList<_cimg_Tt> operator,(const CImg<t>& img) const { - return CImgList<_cimg_Tt>(*this,img); - } - - //! Construct an image list from image instance and an input image list. - /** - Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements: - - A copy of the image instance, at position [\c 0]. - - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()]. - - \param list Input image list that will be appended to the image instance. - \note - - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument. - **/ - template<typename t> - CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const { - return CImgList<_cimg_Tt>(list,false).insert(*this,0); - } - - //! Split image along specified axis. - /** - Return a new list of images (\c CImgList instance) containing the splitted components - of the instance image along the specified axis. - \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c') - \note - - Similar to get_split(char,int) const, with default second argument. - \par Example - \code - const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image - const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels - (img,list).display(); - \endcode - \image html ref_operator_less.jpg - **/ - CImgList<T> operator<(const char axis) const { - return get_split(axis); - } - - //@} - //------------------------------------- - // - //! \name Instance Characteristics - //@{ - //------------------------------------- - - //! Return the type of image pixel values as a C string. - /** - Return a \c char* string containing the usual type name of the image pixel values - (i.e. a stringified version of the template parameter \c T). - \note - - The returned string may contain spaces (as in \c "unsigned char"). - - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned. - **/ - static const char* pixel_type() { - return cimg::type<T>::string(); - } - - //! Return the number of image columns. - /** - Return the image width, i.e. the image dimension along the X-axis. - \note - - The width() of an empty image is equal to \c 0. - - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations. - - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int. - Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving - \c unsigned \c int variables. - Access to the initial \c unsigned \c int variable is possible (though not recommended) by - <tt>(*this)._width</tt>. - **/ - int width() const { - return (int)_width; - } - - //! Return the number of image rows. - /** - Return the image height, i.e. the image dimension along the Y-axis. - \note - - The height() of an empty image is equal to \c 0. - - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int. - Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving - \c unsigned \c int variables. - Access to the initial \c unsigned \c int variable is possible (though not recommended) by - <tt>(*this)._height</tt>. - **/ - int height() const { - return (int)_height; - } - - //! Return the number of image slices. - /** - Return the image depth, i.e. the image dimension along the Z-axis. - \note - - The depth() of an empty image is equal to \c 0. - - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image - is said to be \e volumetric. - - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int. - Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving - \c unsigned \c int variables. - Access to the initial \c unsigned \c int variable is possible (though not recommended) by - <tt>(*this)._depth</tt>. - **/ - int depth() const { - return (int)_depth; - } - - //! Return the number of image channels. - /** - Return the number of image channels, i.e. the image dimension along the C-axis. - \note - - The spectrum() of an empty image is equal to \c 0. - - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3 - for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel). - The number of channels of an image instance is not limited. The meaning of the pixel values is not linked - up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image). - - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int. - Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving - \c unsigned \c int variables. - Access to the initial \c unsigned \c int variable is possible (though not recommended) by - <tt>(*this)._spectrum</tt>. - **/ - int spectrum() const { - return (int)_spectrum; - } - - //! Return the total number of pixel values. - /** - Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>, - i.e. the total number of values of type \c T in the pixel buffer of the image instance. - \note - - The size() of an empty image is equal to \c 0. - - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to - <tt>size()*sizeof(T)</tt>. - \par Example - \code - const CImg<float> img(100,100,1,3); // Construct new 100x100 color image - if (img.size()==30000) // Test succeeds - std::printf("Pixel buffer uses %lu bytes", - img.size()*sizeof(float)); - \endcode - **/ - ulongT size() const { - return (ulongT)_width*_height*_depth*_spectrum; - } - - //! Return a pointer to the first pixel value. - /** - Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance, - whether the instance is \c const or not. - \note - - The data() of an empty image is equal to \c 0 (null pointer). - - The allocated pixel buffer for the image instance starts from \c data() - and goes to <tt>data()+\ref size() - 1</tt> (included). - - To get the pointer to one particular location of the pixel buffer, use - data(unsigned int,unsigned int,unsigned int,unsigned int) instead. - **/ - T* data() { - return _data; - } - - //! Return a pointer to the first pixel value \const. - const T* data() const { - return _data; - } - - //! Return a pointer to a located pixel value. - /** - Return a \c T*, or a \c const \c T* pointer to the value located at (\c x,\c y,\c z,\c c) in the pixel buffer - of the image instance, - whether the instance is \c const or not. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note - - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same - properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). - **/ -#if cimg_verbosity>=3 - T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { - const ulongT off = (ulongT)offset(x,y,z,c); - if (off>=size()) - cimg::warn(_cimg_instance - "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].", - cimg_instance, - x,y,z,c,off); - return _data + off; - } - - //! Return a pointer to a located pixel value \const. - const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { - return const_cast<CImg<T>*>(this)->data(x,y,z,c); - } -#else - T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { - return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; - } - - const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { - return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; - } -#endif - - //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer. - /** - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note - - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>. - Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). - \par Example - \code - const CImg<float> img(100,100,1,3); // Define a 100x100 RGB-color image - const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10) - const float val = img[off]; // Get the blue value of this pixel - \endcode - **/ - longT offset(const int x, const int y=0, const int z=0, const int c=0) const { - return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth; - } - - //! Return a CImg<T>::iterator pointing to the first pixel value. - /** - \note - - Equivalent to data(). - - It has been mainly defined for compatibility with STL naming conventions. - **/ - iterator begin() { - return _data; - } - - //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const. - const_iterator begin() const { - return _data; - } - - //! Return a CImg<T>::iterator pointing next to the last pixel value. - /** - \note - - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>. - - It has been mainly defined for compatibility with STL naming conventions. - \warning - - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer. - Trying to read or write the content of the returned iterator will probably result in a crash. - Use it mainly as a strict upper bound for a CImg<T>::iterator. - \par Example - \code - CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image - // 'img.end()' used below as an upper bound for the iterator. - for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it) - *it = 0; - \endcode - **/ - iterator end() { - return _data + size(); - } - - //! Return a CImg<T>::iterator pointing next to the last pixel value \const. - const_iterator end() const { - return _data + size(); - } - - //! Return a reference to the first pixel value. - /** - \note - - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>. - - It has been mainly defined for compatibility with STL naming conventions. - **/ - T& front() { - return *_data; - } - - //! Return a reference to the first pixel value \const. - const T& front() const { - return *_data; - } - - //! Return a reference to the last pixel value. - /** - \note - - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or - <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>. - - It has been mainly defined for compatibility with STL naming conventions. - **/ - T& back() { - return *(_data + size() - 1); - } - - //! Return a reference to the last pixel value \const. - const T& back() const { - return *(_data + size() - 1); - } - - //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions. - /** - Return a reference to the pixel value of the image instance located at a specified \c offset, - or to a specified default value in case of out-of-bounds access. - \param offset Offset to the desired pixel value. - \param out_value Default value returned if \c offset is outside image bounds. - \note - - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset - is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value - is safely returned instead. - - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when - you are \e not sure about the validity of the specified pixel offset. - **/ - T& at(const int offset, const T& out_value) { - return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset]; - } - - //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const. - T at(const int offset, const T& out_value) const { - return (offset<0 || offset>=(int)size())?out_value:(*this)[offset]; - } - - //! Access to a pixel value at a specified offset, using Neumann boundary conditions. - /** - Return a reference to the pixel value of the image instance located at a specified \c offset, - or to the nearest pixel location in the image instance in case of out-of-bounds access. - \param offset Offset to the desired pixel value. - \note - - Similar to at(int,const T), except that an out-of-bounds access returns the value of the - nearest pixel in the image instance, regarding the specified offset, i.e. - - If \c offset<0, then \c img[0] is returned. - - If \c offset>=img.size(), then \c img[img.size() - 1] is returned. - - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when - you are \e not sure about the validity of the specified pixel offset. - - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int). - **/ - T& at(const int offset) { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "at(): Empty instance.", - cimg_instance); - return _at(offset); - } - - T& _at(const int offset) { - const unsigned int siz = (unsigned int)size(); - return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; - } - - //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const. - const T& at(const int offset) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "at(): Empty instance.", - cimg_instance); - return _at(offset); - } - - const T& _at(const int offset) const { - const unsigned int siz = (unsigned int)size(); - return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; - } - - //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate. - /** - Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), - or to a specified default value in case of out-of-bounds access along the X-axis. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds. - \note - - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value - \c out_value. - - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when - you are \e not sure about the validity of the specified pixel coordinates. - \warning - - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. - **/ - T& atX(const int x, const int y, const int z, const int c, const T& out_value) { - return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); - } - - //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const. - T atX(const int x, const int y, const int z, const int c, const T& out_value) const { - return (x<0 || x>=width())?out_value:(*this)(x,y,z,c); - } - - //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate. - /** - Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), - or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note - - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the - nearest pixel in the image instance, regarding the specified X-coordinate. - - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when - you are \e not sure about the validity of the specified pixel coordinates. - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _at(int,int,int,int). - \warning - - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. - **/ - T& atX(const int x, const int y=0, const int z=0, const int c=0) { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atX(): Empty instance.", - cimg_instance); - return _atX(x,y,z,c); - } - - T& _atX(const int x, const int y=0, const int z=0, const int c=0) { - return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); - } - - //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const. - const T& atX(const int x, const int y=0, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atX(): Empty instance.", - cimg_instance); - return _atX(x,y,z,c); - } - - const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const { - return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); - } - - //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates. - /** - Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates. - **/ - T& atXY(const int x, const int y, const int z, const int c, const T& out_value) { - return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); - } - - //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const. - T atXY(const int x, const int y, const int z, const int c, const T& out_value) const { - return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c); - } - - //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates. - /** - Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _atXY(int,int,int,int). - **/ - T& atXY(const int x, const int y, const int z=0, const int c=0) { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atXY(): Empty instance.", - cimg_instance); - return _atXY(x,y,z,c); - } - - T& _atXY(const int x, const int y, const int z=0, const int c=0) { - return (*this)(cimg::cut(x,0,width() - 1), - cimg::cut(y,0,height() - 1),z,c); - } - - //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const. - const T& atXY(const int x, const int y, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atXY(): Empty instance.", - cimg_instance); - return _atXY(x,y,z,c); - } - - const T& _atXY(const int x, const int y, const int z=0, const int c=0) const { - return (*this)(cimg::cut(x,0,width() - 1), - cimg::cut(y,0,height() - 1),z,c); - } - - //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates. - /** - Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on - X,Y and Z-coordinates. - **/ - T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) { - return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())? - (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); - } - - //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const. - T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const { - return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c); - } - - //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates. - /** - Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _atXYZ(int,int,int,int). - **/ - T& atXYZ(const int x, const int y, const int z, const int c=0) { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atXYZ(): Empty instance.", - cimg_instance); - return _atXYZ(x,y,z,c); - } - - T& _atXYZ(const int x, const int y, const int z, const int c=0) { - return (*this)(cimg::cut(x,0,width() - 1), - cimg::cut(y,0,height() - 1), - cimg::cut(z,0,depth() - 1),c); - } - - //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const. - const T& atXYZ(const int x, const int y, const int z, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atXYZ(): Empty instance.", - cimg_instance); - return _atXYZ(x,y,z,c); - } - - const T& _atXYZ(const int x, const int y, const int z, const int c=0) const { - return (*this)(cimg::cut(x,0,width() - 1), - cimg::cut(y,0,height() - 1), - cimg::cut(z,0,depth() - 1),c); - } - - //! Access to a pixel value, using Dirichlet boundary conditions. - /** - Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all - X,Y,Z and C-coordinates. - **/ - T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) { - return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())? - (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); - } - - //! Access to a pixel value, using Dirichlet boundary conditions \const. - T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const { - return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value: - (*this)(x,y,z,c); - } - - //! Access to a pixel value, using Neumann boundary conditions. - /** - Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _atXYZC(int,int,int,int). - **/ - T& atXYZC(const int x, const int y, const int z, const int c) { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atXYZC(): Empty instance.", - cimg_instance); - return _atXYZC(x,y,z,c); - } - - T& _atXYZC(const int x, const int y, const int z, const int c) { - return (*this)(cimg::cut(x,0,width() - 1), - cimg::cut(y,0,height() - 1), - cimg::cut(z,0,depth() - 1), - cimg::cut(c,0,spectrum() - 1)); - } - - //! Access to a pixel value, using Neumann boundary conditions \const. - const T& atXYZC(const int x, const int y, const int z, const int c) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "atXYZC(): Empty instance.", - cimg_instance); - return _atXYZC(x,y,z,c); - } - - const T& _atXYZC(const int x, const int y, const int z, const int c) const { - return (*this)(cimg::cut(x,0,width() - 1), - cimg::cut(y,0,height() - 1), - cimg::cut(z,0,depth() - 1), - cimg::cut(c,0,spectrum() - 1)); - } - - //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate. - /** - Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), - or a specified default value in case of out-of-bounds access along the X-axis. - \param fx X-coordinate of the pixel value (float-valued). - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. - \note - - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by - a linear interpolation along the X-axis, if corresponding coordinates are not integers. - - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. - \warning - - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. - **/ - Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { - const int - x = (int)fx - (fx>=0?0:1), nx = x + 1; - const float - dx = fx - x; - const Tfloat - Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value); - return Ic + dx*(In - Ic); - } - - //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate. - /** - Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), - or the value of the nearest pixel location in the image instance in case of out-of-bounds access along - the X-axis. - \param fx X-coordinate of the pixel value (float-valued). - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note - - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns - the value of the nearest pixel in the image instance, regarding the specified X-coordinate. - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _linear_atX(float,int,int,int). - \warning - - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. - **/ - Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "linear_atX(): Empty instance.", - cimg_instance); - - return _linear_atX(fx,y,z,c); - } - - Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { - const float - nfx = cimg::cut(fx,0,width() - 1); - const unsigned int - x = (unsigned int)nfx; - const float - dx = nfx - x; - const unsigned int - nx = dx>0?x + 1:x; - const Tfloat - Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c); - return Ic + dx*(In - Ic); - } - - //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates. - /** - Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the - boundary checking are achieved both for X and Y-coordinates. - **/ - Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { - const int - x = (int)fx - (fx>=0?0:1), nx = x + 1, - y = (int)fy - (fy>=0?0:1), ny = y + 1; - const float - dx = fx - x, - dy = fy - y; - const Tfloat - Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value), - Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value); - return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc); - } - - //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates. - /** - Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking - are achieved both for X and Y-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _linear_atXY(float,float,int,int). - **/ - Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "linear_atXY(): Empty instance.", - cimg_instance); - - return _linear_atXY(fx,fy,z,c); - } - - Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { - const float - nfx = cimg::cut(fx,0,width() - 1), - nfy = cimg::cut(fy,0,height() - 1); - const unsigned int - x = (unsigned int)nfx, - y = (unsigned int)nfy; - const float - dx = nfx - x, - dy = nfy - y; - const unsigned int - nx = dx>0?x + 1:x, - ny = dy>0?y + 1:y; - const Tfloat - Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), - Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); - return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc); - } - - //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. - /** - Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the - boundary checking are achieved both for X,Y and Z-coordinates. - **/ - Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { - const int - x = (int)fx - (fx>=0?0:1), nx = x + 1, - y = (int)fy - (fy>=0?0:1), ny = y + 1, - z = (int)fz - (fz>=0?0:1), nz = z + 1; - const float - dx = fx - x, - dy = fy - y, - dz = fz - z; - const Tfloat - Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), - Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), - Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), - Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value); - return Iccc + - dx*(Incc - Iccc + - dy*(Iccc + Innc - Icnc - Incc + - dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) + - dz*(Iccc + Incn - Iccn - Incc)) + - dy*(Icnc - Iccc + - dz*(Iccc + Icnn - Iccn - Icnc)) + - dz*(Iccn - Iccc); - } - - //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. - /** - Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking - are achieved both for X,Y and Z-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _linear_atXYZ(float,float,float,int). - **/ - Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "linear_atXYZ(): Empty instance.", - cimg_instance); - - return _linear_atXYZ(fx,fy,fz,c); - } - - Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { - const float - nfx = cimg::cut(fx,0,width() - 1), - nfy = cimg::cut(fy,0,height() - 1), - nfz = cimg::cut(fz,0,depth() - 1); - const unsigned int - x = (unsigned int)nfx, - y = (unsigned int)nfy, - z = (unsigned int)nfz; - const float - dx = nfx - x, - dy = nfy - y, - dz = nfz - z; - const unsigned int - nx = dx>0?x + 1:x, - ny = dy>0?y + 1:y, - nz = dz>0?z + 1:z; - const Tfloat - Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c), - Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), - Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), - Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); - return Iccc + - dx*(Incc - Iccc + - dy*(Iccc + Innc - Icnc - Incc + - dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) + - dz*(Iccc + Incn - Iccn - Incc)) + - dy*(Icnc - Iccc + - dz*(Iccc + Icnn - Iccn - Icnc)) + - dz*(Iccn - Iccc); - } - - //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates. - /** - Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the - boundary checking are achieved for all X,Y,Z and C-coordinates. - **/ - Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const { - const int - x = (int)fx - (fx>=0?0:1), nx = x + 1, - y = (int)fy - (fy>=0?0:1), ny = y + 1, - z = (int)fz - (fz>=0?0:1), nz = z + 1, - c = (int)fc - (fc>=0?0:1), nc = c + 1; - const float - dx = fx - x, - dy = fy - y, - dz = fz - z, - dc = fc - c; - const Tfloat - Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value), - Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value), - Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value), - Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value), - Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value), - Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value), - Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value), - Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value); - return Icccc + - dx*(Inccc - Icccc + - dy*(Icccc + Inncc - Icncc - Inccc + - dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + - dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - - Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + - dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + - dz*(Icccc + Incnc - Iccnc - Inccc + - dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + - dc*(Icccc + Inccn - Inccc - Icccn)) + - dy*(Icncc - Icccc + - dz*(Icccc + Icnnc - Iccnc - Icncc + - dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + - dc*(Icccc + Icncn - Icncc - Icccn)) + - dz*(Iccnc - Icccc + - dc*(Icccc + Iccnn - Iccnc - Icccn)) + - dc*(Icccn -Icccc); - } - - //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates. - /** - Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking - are achieved for all X,Y,Z and C-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _linear_atXYZC(float,float,float,float). - **/ - Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "linear_atXYZC(): Empty instance.", - cimg_instance); - - return _linear_atXYZC(fx,fy,fz,fc); - } - - Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { - const float - nfx = cimg::cut(fx,0,width() - 1), - nfy = cimg::cut(fy,0,height() - 1), - nfz = cimg::cut(fz,0,depth() - 1), - nfc = cimg::cut(fc,0,spectrum() - 1); - const unsigned int - x = (unsigned int)nfx, - y = (unsigned int)nfy, - z = (unsigned int)nfz, - c = (unsigned int)nfc; - const float - dx = nfx - x, - dy = nfy - y, - dz = nfz - z, - dc = nfc - c; - const unsigned int - nx = dx>0?x + 1:x, - ny = dy>0?y + 1:y, - nz = dz>0?z + 1:z, - nc = dc>0?c + 1:c; - const Tfloat - Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), - Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), - Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), - Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), - Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), - Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), - Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), - Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); - return Icccc + - dx*(Inccc - Icccc + - dy*(Icccc + Inncc - Icncc - Inccc + - dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + - dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - - Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + - dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + - dz*(Icccc + Incnc - Iccnc - Inccc + - dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + - dc*(Icccc + Inccn - Inccc - Icccn)) + - dy*(Icncc - Icccc + - dz*(Icccc + Icnnc - Iccnc - Icncc + - dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + - dc*(Icccc + Icncn - Icncc - Icccn)) + - dz*(Iccnc - Icccc + - dc*(Icccc + Iccnn - Iccnc - Icccn)) + - dc*(Icccn - Icccc); - } - - //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. - /** - Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), - or a specified default value in case of out-of-bounds access along the X-axis. - The cubic interpolation uses Hermite splines. - \param fx d X-coordinate of the pixel value (float-valued). - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. - \note - - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is - approximated by a \e cubic interpolation along the X-axis. - - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. - \warning - - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. - **/ - Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { - const int - x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2; - const float - dx = fx - x; - const Tfloat - Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value), - In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value); - return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); - } - - //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. - /** - Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the - min/max range of the datatype \c T. - **/ - T cubic_cut_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { - return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value)); - } - - //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. - /** - Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), - or the value of the nearest pixel location in the image instance in case of out-of-bounds access - along the X-axis. The cubic interpolation uses Hermite splines. - \param fx X-coordinate of the pixel value (float-valued). - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note - - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is - approximated by a cubic interpolation along the X-axis. - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _cubic_atX(float,int,int,int). - \warning - - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. - **/ - Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "cubic_atX(): Empty instance.", - cimg_instance); - return _cubic_atX(fx,y,z,c); - } - - Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { - const float - nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1); - const int - x = (int)nfx; - const float - dx = nfx - x; - const int - px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2; - const Tfloat - Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), - In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); - return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); - } - - //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. - /** - Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the - min/max range of the datatype \c T. - **/ - T cubic_cut_atX(const float fx, const int y, const int z, const int c) const { - return cimg::type<T>::cut(cubic_atX(fx,y,z,c)); - } - - T _cubic_cut_atX(const float fx, const int y, const int z, const int c) const { - return cimg::type<T>::cut(_cubic_atX(fx,y,z,c)); - } - - //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. - /** - Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking - are achieved both for X and Y-coordinates. - **/ - Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { - const int - x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, - y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2; - const float dx = fx - x, dy = fy - y; - const Tfloat - Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value), - Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value), - Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), - Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value), - Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value), - Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), - Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value), - Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value), - In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), - Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value), - Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value), - Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); - return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); - } - - //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates. - /** - Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the - min/max range of the datatype \c T. - **/ - T cubic_cut_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { - return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value)); - } - - //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. - /** - Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking - are achieved for both X and Y-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _cubic_atXY(float,float,int,int). - **/ - Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "cubic_atXY(): Empty instance.", - cimg_instance); - return _cubic_atXY(fx,fy,z,c); - } - - Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { - const float - nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), - nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1); - const int x = (int)nfx, y = (int)nfy; - const float dx = nfx - x, dy = nfy - y; - const int - px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2, - py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2; - const Tfloat - Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), - Iap = (Tfloat)(*this)(ax,py,z,c), - Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), - Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), - Iac = (Tfloat)(*this)(ax,y,z,c), - Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), - Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), - Ian = (Tfloat)(*this)(ax,ny,z,c), - In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), - Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), - Iaa = (Tfloat)(*this)(ax,ay,z,c), - Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); - return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); - } - - //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates. - /** - Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the - min/max range of the datatype \c T. - **/ - T cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const { - return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c)); - } - - T _cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const { - return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c)); - } - - //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. - /** - Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking - are achieved both for X,Y and Z-coordinates. - **/ - Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { - const int - x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, - y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2, - z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2; - const float dx = fx - x, dy = fy - y, dz = fz - z; - const Tfloat - Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value), - Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value), - Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + - dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), - Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value), - Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value), - Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + - dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), - Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value), - Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value), - Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + - dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), - Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value), - Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value), - Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + - dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), - Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + - dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), - Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value), - Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value), - Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + - dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), - Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value), - Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value), - Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + - dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), - Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), - Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value), - Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + - dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), - Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value), - Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value), - Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + - dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), - Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + - dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), - Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value), - Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value), - Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + - dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), - Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value), - Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value), - Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + - dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), - Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), - Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value), - Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + - dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), - Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value), - Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value), - Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + - dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), - In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + - dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), - Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value), - Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value), - Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + - dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), - Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value), - Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value), - Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + - dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), - Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value), - Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value), - Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + - dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), - Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value), - Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value), - Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + - dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), - Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + - dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); - return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); - } - - //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates. - /** - Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay - in the min/max range of the datatype \c T. - **/ - T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { - return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value)); - } - - //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. - /** - Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking - are achieved both for X,Y and Z-coordinates. - \note - - If you know your image instance is \e not empty, you may rather use the slightly faster method - \c _cubic_atXYZ(float,float,float,int). - **/ - Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "cubic_atXYZ(): Empty instance.", - cimg_instance); - return _cubic_atXYZ(fx,fy,fz,c); - } - - Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { - const float - nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), - nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1), - nfz = cimg::type<float>::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1); - const int x = (int)nfx, y = (int)nfy, z = (int)nfz; - const float dx = nfx - x, dy = nfy - y, dz = nfz - z; - const int - px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2, - py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2, - pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2; - const Tfloat - Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), - Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), - Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + - dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), - Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), - Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), - Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + - dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), - Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), - Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), - Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + - dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), - Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), - Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), - Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + - dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), - Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + - dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), - Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), - Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), - Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + - dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), - Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), - Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), - Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + - dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), - Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), - Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), - Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + - dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), - Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), - Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), - Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + - dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), - Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + - dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), - Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), - Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), - Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + - dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), - Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), - Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), - Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + - dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), - Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), - Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), - Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + - dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), - Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), - Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), - Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + - dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), - In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + - dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), - Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), - Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), - Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + - dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), - Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), - Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), - Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + - dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), - Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), - Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), - Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + - dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), - Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), - Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), - Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + - dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), - Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + - dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); - return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); - } - - //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates. - /** - Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the - min/max range of the datatype \c T. - **/ - T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const { - return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c)); - } - - T _cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const { - return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c)); - } - - //! Set pixel value, using linear interpolation for the X-coordinates. - /** - Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that - the value is spread amongst several neighbors if the pixel coordinates are float-valued. - \param value Pixel value to set. - \param fx X-coordinate of the pixel value (float-valued). - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image - pixel(s). - \return A reference to the current image instance. - \note - - Calling this method with out-of-bounds coordinates does nothing. - **/ - CImg<T>& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0, - const bool is_added=false) { - const int - x = (int)fx - (fx>=0?0:1), nx = x + 1; - const float - dx = fx - x; - if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) { - if (x>=0 && x<width()) { - const float w1 = 1 - dx, w2 = is_added?1:(1 - w1); - (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c)); - } - if (nx>=0 && nx<width()) { - const float w1 = dx, w2 = is_added?1:(1 - w1); - (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c)); - } - } - return *this; - } - - //! Set pixel value, using linear interpolation for the X and Y-coordinates. - /** - Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation - is achieved both for X and Y-coordinates. - **/ - CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0, - const bool is_added=false) { - const int - x = (int)fx - (fx>=0?0:1), nx = x + 1, - y = (int)fy - (fy>=0?0:1), ny = y + 1; - const float - dx = fx - x, - dy = fy - y; - if (z>=0 && z<depth() && c>=0 && c<spectrum()) { - if (y>=0 && y<height()) { - if (x>=0 && x<width()) { - const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1); - (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c)); - } - if (nx>=0 && nx<width()) { - const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1); - (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c)); - } - } - if (ny>=0 && ny<height()) { - if (x>=0 && x<width()) { - const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1); - (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c)); - } - if (nx>=0 && nx<width()) { - const float w1 = dx*dy, w2 = is_added?1:(1 - w1); - (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c)); - } - } - } - return *this; - } - - //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates. - /** - Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation - is achieved both for X,Y and Z-coordinates. - **/ - CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0, - const bool is_added=false) { - const int - x = (int)fx - (fx>=0?0:1), nx = x + 1, - y = (int)fy - (fy>=0?0:1), ny = y + 1, - z = (int)fz - (fz>=0?0:1), nz = z + 1; - const float - dx = fx - x, - dy = fy - y, - dz = fz - z; - if (c>=0 && c<spectrum()) { - if (z>=0 && z<depth()) { - if (y>=0 && y<height()) { - if (x>=0 && x<width()) { - const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1); - (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c)); - } - if (nx>=0 && nx<width()) { - const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1); - (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c)); - } - } - if (ny>=0 && ny<height()) { - if (x>=0 && x<width()) { - const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1); - (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c)); - } - if (nx>=0 && nx<width()) { - const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1); - (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c)); - } - } - } - if (nz>=0 && nz<depth()) { - if (y>=0 && y<height()) { - if (x>=0 && x<width()) { - const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1); - (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c)); - } - if (nx>=0 && nx<width()) { - const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1); - (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c)); - } - } - if (ny>=0 && ny<height()) { - if (x>=0 && x<width()) { - const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1); - (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c)); - } - if (nx>=0 && nx<width()) { - const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1); - (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c)); - } - } - } - } - return *this; - } - - //! Return a C-string containing a list of all values of the image instance. - /** - Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values - of the image instance (written in base 10), separated by specified \c separator character. - \param separator A \c char character which specifies the separator between values in the returned C-string. - \param max_size Maximum size of the returned image (or \c 0 if no limits are set). - \param format For float/double-values, tell the printf format used to generate the Ascii representation - of the numbers (or \c 0 for default representation). - \note - - The returned image is never empty. - - For an empty image instance, the returned string is <tt>""</tt>. - - If \c max_size is equal to \c 0, there are no limits on the size of the returned string. - - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off - and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>. - **/ - CImg<charT> value_string(const char separator=',', const unsigned int max_size=0, - const char *const format=0) const { - if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0); - CImgList<charT> items; - CImg<charT> s_item(256); *s_item = 0; - const T *ptrs = _data; - unsigned int string_size = 0; - const char *const _format = format?format:cimg::type<T>::format(); - for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) { - const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format, - cimg::type<T>::format(*(ptrs++))); - CImg<charT> item(s_item._data,printed_size); - item[printed_size - 1] = separator; - item.move_to(items); - if (max_size) string_size+=printed_size; - } - CImg<charT> res; - (items>'x').move_to(res); - if (max_size && res._width>=max_size) res.crop(0,max_size - 1); - res.back() = 0; - return res; - } - - //@} - //------------------------------------- - // - //! \name Instance Checking - //@{ - //------------------------------------- - - //! Test shared state of the pixel buffer. - /** - Return \c true if image instance has a shared memory buffer, and \c false otherwise. - \note - - A shared image do not own his pixel buffer data() and will not deallocate it on destruction. - - Most of the time, a \c CImg<T> image instance will \e not be shared. - - A shared image can only be obtained by a limited set of constructors and methods (see list below). - **/ - bool is_shared() const { - return _is_shared; - } - - //! Test if image instance is empty. - /** - Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions - \c 0 x \c 0 x \c 0 x \c 0 and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise. - **/ - bool is_empty() const { - return !(_data && _width && _height && _depth && _spectrum); - } - - //! Test if image instance contains a 'inf' value. - /** - Return \c true, if image instance contains a 'inf' value, and \c false otherwise. - **/ - bool is_inf() const { - if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true; - return false; - } - - //! Test if image instance contains a NaN value. - /** - Return \c true, if image instance contains a NaN value, and \c false otherwise. - **/ - bool is_nan() const { - if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true; - return false; - } - - //! Test if image width is equal to specified value. - bool is_sameX(const unsigned int size_x) const { - return _width==size_x; - } - - //! Test if image width is equal to specified value. - template<typename t> - bool is_sameX(const CImg<t>& img) const { - return is_sameX(img._width); - } - - //! Test if image width is equal to specified value. - bool is_sameX(const CImgDisplay& disp) const { - return is_sameX(disp._width); - } - - //! Test if image height is equal to specified value. - bool is_sameY(const unsigned int size_y) const { - return _height==size_y; - } - - //! Test if image height is equal to specified value. - template<typename t> - bool is_sameY(const CImg<t>& img) const { - return is_sameY(img._height); - } - - //! Test if image height is equal to specified value. - bool is_sameY(const CImgDisplay& disp) const { - return is_sameY(disp._height); - } - - //! Test if image depth is equal to specified value. - bool is_sameZ(const unsigned int size_z) const { - return _depth==size_z; - } - - //! Test if image depth is equal to specified value. - template<typename t> - bool is_sameZ(const CImg<t>& img) const { - return is_sameZ(img._depth); - } - - //! Test if image spectrum is equal to specified value. - bool is_sameC(const unsigned int size_c) const { - return _spectrum==size_c; - } - - //! Test if image spectrum is equal to specified value. - template<typename t> - bool is_sameC(const CImg<t>& img) const { - return is_sameC(img._spectrum); - } - - //! Test if image width and height are equal to specified values. - /** - Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified. - **/ - bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const { - return _width==size_x && _height==size_y; - } - - //! Test if image width and height are the same as that of another image. - /** - Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameXY(const CImg<t>& img) const { - return is_sameXY(img._width,img._height); - } - - //! Test if image width and height are the same as that of an existing display window. - /** - Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified. - **/ - bool is_sameXY(const CImgDisplay& disp) const { - return is_sameXY(disp._width,disp._height); - } - - //! Test if image width and depth are equal to specified values. - /** - Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified. - **/ - bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const { - return _width==size_x && _depth==size_z; - } - - //! Test if image width and depth are the same as that of another image. - /** - Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameXZ(const CImg<t>& img) const { - return is_sameXZ(img._width,img._depth); - } - - //! Test if image width and spectrum are equal to specified values. - /** - Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified. - **/ - bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const { - return _width==size_x && _spectrum==size_c; - } - - //! Test if image width and spectrum are the same as that of another image. - /** - Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameXC(const CImg<t>& img) const { - return is_sameXC(img._width,img._spectrum); - } - - //! Test if image height and depth are equal to specified values. - /** - Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified. - **/ - bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const { - return _height==size_y && _depth==size_z; - } - - //! Test if image height and depth are the same as that of another image. - /** - Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameYZ(const CImg<t>& img) const { - return is_sameYZ(img._height,img._depth); - } - - //! Test if image height and spectrum are equal to specified values. - /** - Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified. - **/ - bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const { - return _height==size_y && _spectrum==size_c; - } - - //! Test if image height and spectrum are the same as that of another image. - /** - Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameYC(const CImg<t>& img) const { - return is_sameYC(img._height,img._spectrum); - } - - //! Test if image depth and spectrum are equal to specified values. - /** - Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified. - **/ - bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const { - return _depth==size_z && _spectrum==size_c; - } - - //! Test if image depth and spectrum are the same as that of another image. - /** - Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameZC(const CImg<t>& img) const { - return is_sameZC(img._depth,img._spectrum); - } - - //! Test if image width, height and depth are equal to specified values. - /** - Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified. - **/ - bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const { - return is_sameXY(size_x,size_y) && _depth==size_z; - } - - //! Test if image width, height and depth are the same as that of another image. - /** - Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameXYZ(const CImg<t>& img) const { - return is_sameXYZ(img._width,img._height,img._depth); - } - - //! Test if image width, height and spectrum are equal to specified values. - /** - Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. - **/ - bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const { - return is_sameXY(size_x,size_y) && _spectrum==size_c; - } - - //! Test if image width, height and spectrum are the same as that of another image. - /** - Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameXYC(const CImg<t>& img) const { - return is_sameXYC(img._width,img._height,img._spectrum); - } - - //! Test if image width, depth and spectrum are equal to specified values. - /** - Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. - **/ - bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const { - return is_sameXZ(size_x,size_z) && _spectrum==size_c; - } - - //! Test if image width, depth and spectrum are the same as that of another image. - /** - Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameXZC(const CImg<t>& img) const { - return is_sameXZC(img._width,img._depth,img._spectrum); - } - - //! Test if image height, depth and spectrum are equal to specified values. - /** - Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. - **/ - bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const { - return is_sameYZ(size_y,size_z) && _spectrum==size_c; - } - - //! Test if image height, depth and spectrum are the same as that of another image. - /** - Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameYZC(const CImg<t>& img) const { - return is_sameYZC(img._height,img._depth,img._spectrum); - } - - //! Test if image width, height, depth and spectrum are equal to specified values. - /** - Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both - verified. - **/ - bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c) const { - return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c; - } - - //! Test if image width, height, depth and spectrum are the same as that of another image. - /** - Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified. - **/ - template<typename t> - bool is_sameXYZC(const CImg<t>& img) const { - return is_sameXYZC(img._width,img._height,img._depth,img._spectrum); - } - - //! Test if specified coordinates are inside image bounds. - /** - Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance, - and \c false otherwise. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note - - Return \c true only if all these conditions are verified: - - The image instance is \e not empty. - - <tt>0<=x<=\ref width() - 1</tt>. - - <tt>0<=y<=\ref height() - 1</tt>. - - <tt>0<=z<=\ref depth() - 1</tt>. - - <tt>0<=c<=\ref spectrum() - 1</tt>. - **/ - bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const { - return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum(); - } - - //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates. - /** - Return \c true, if specified reference refers to a pixel value inside bounds of the image instance, - and \c false otherwise. - \param pixel Reference to pixel value to test. - \param[out] x X-coordinate of the pixel value, if test succeeds. - \param[out] y Y-coordinate of the pixel value, if test succeeds. - \param[out] z Z-coordinate of the pixel value, if test succeeds. - \param[out] c C-coordinate of the pixel value, if test succeeds. - \note - - Useful to convert an offset to a buffer value into pixel value coordinates: - \code - const CImg<float> img(100,100,1,3); // Construct a 100x100 RGB color image - const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0) - unsigned int x,y,z,c; - if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates - std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n", - offset,x,y,z,c); - } - \endcode - **/ - template<typename t> - bool contains(const T& pixel, t& x, t& y, t& z, t& c) const { - const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; - const T *const ppixel = &pixel; - if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; - ulongT off = (ulongT)(ppixel - _data); - const ulongT nc = off/whd; - off%=whd; - const ulongT nz = off/wh; - off%=wh; - const ulongT ny = off/_width, nx = off%_width; - x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc; - return true; - } - - //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates. - /** - Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set. - **/ - template<typename t> - bool contains(const T& pixel, t& x, t& y, t& z) const { - const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; - const T *const ppixel = &pixel; - if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; - ulongT off = ((ulongT)(ppixel - _data))%whd; - const ulongT nz = off/wh; - off%=wh; - const ulongT ny = off/_width, nx = off%_width; - x = (t)nx; y = (t)ny; z = (t)nz; - return true; - } - - //! Test if pixel value is inside image bounds and get its X and Y-coordinates. - /** - Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set. - **/ - template<typename t> - bool contains(const T& pixel, t& x, t& y) const { - const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum; - const T *const ppixel = &pixel; - if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; - ulongT off = ((unsigned int)(ppixel - _data))%wh; - const ulongT ny = off/_width, nx = off%_width; - x = (t)nx; y = (t)ny; - return true; - } - - //! Test if pixel value is inside image bounds and get its X-coordinate. - /** - Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set. - **/ - template<typename t> - bool contains(const T& pixel, t& x) const { - const T *const ppixel = &pixel; - if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false; - x = (t)(((ulongT)(ppixel - _data))%_width); - return true; - } - - //! Test if pixel value is inside image bounds. - /** - Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set. - **/ - bool contains(const T& pixel) const { - const T *const ppixel = &pixel; - return !is_empty() && ppixel>=_data && ppixel<_data + size(); - } - - //! Test if pixel buffers of instance and input images overlap. - /** - Return \c true, if pixel buffers attached to image instance and input image \c img overlap, - and \c false otherwise. - \param img Input image to compare with. - \note - - Buffer overlapping may happen when manipulating \e shared images. - - If two image buffers overlap, operating on one of the image will probably modify the other one. - - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others. - \par Example - \code - const CImg<float> - img1("reference.jpg"), // Load RGB-color image - img2 = img1.get_shared_channel(1); // Get shared version of the green channel - if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps - std::printf("Buffers overlap!\n"); - } - \endcode - **/ - template<typename t> - bool is_overlapped(const CImg<t>& img) const { - const ulongT csiz = size(), isiz = img.size(); - return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz)); - } - - //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object. - /** - Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a - valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image. - \param primitives List of primitives of the 3D object. - \param colors List of colors of the 3D object. - \param opacities List (or image) of opacities of the 3D object. - \param full_check Tells if full checking of the 3D object must be performed. - \param[out] error_message C-string to contain the error message, if the test does not succeed. - \note - - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of - each 3D object component is checked. - - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. - **/ - template<typename tp, typename tc, typename to> - bool is_object3d(const CImgList<tp>& primitives, - const CImgList<tc>& colors, - const to& opacities, - const bool full_check=true, - char *const error_message=0) const { - if (error_message) *error_message = 0; - - // Check consistency for the particular case of an empty 3D object. - if (is_empty()) { - if (primitives || colors || opacities) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) defines no vertices but %u primitives, " - "%u colors and %lu opacities", - _width,primitives._width,primitives._width, - colors._width,(unsigned long)opacities.size()); - return false; - } - return true; - } - - // Check consistency of vertices. - if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)", - _width,primitives._width,_width,_height,_depth,_spectrum); - return false; - } - if (colors._width>primitives._width + 1) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) defines %u colors", - _width,primitives._width,colors._width); - return false; - } - if (opacities.size()>primitives._width) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) defines %lu opacities", - _width,primitives._width,(unsigned long)opacities.size()); - return false; - } - if (!full_check) return true; - - // Check consistency of primitives. - cimglist_for(primitives,l) { - const CImg<tp>& primitive = primitives[l]; - const unsigned int psiz = (unsigned int)primitive.size(); - switch (psiz) { - case 1 : { // Point - const unsigned int i0 = (unsigned int)primitive(0); - if (i0>=_width) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) refers to invalid vertex indice %u in " - "point primitive [%u]", - _width,primitives._width,i0,l); - return false; - } - } break; - case 5 : { // Sphere - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1); - if (i0>=_width || i1>=_width) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " - "sphere primitive [%u]", - _width,primitives._width,i0,i1,l); - return false; - } - } break; - case 2 : case 6 : { // Segment - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1); - if (i0>=_width || i1>=_width) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " - "segment primitive [%u]", - _width,primitives._width,i0,i1,l); - return false; - } - } break; - case 3 : case 9 : { // Triangle - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1), - i2 = (unsigned int)primitive(2); - if (i0>=_width || i1>=_width || i2>=_width) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " - "triangle primitive [%u]", - _width,primitives._width,i0,i1,i2,l); - return false; - } - } break; - case 4 : case 12 : { // Quadrangle - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1), - i2 = (unsigned int)primitive(2), - i3 = (unsigned int)primitive(3); - if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " - "quadrangle primitive [%u]", - _width,primitives._width,i0,i1,i2,i3,l); - return false; - } - } break; - default : - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) defines an invalid primitive [%u] of size %u", - _width,primitives._width,l,(unsigned int)psiz); - return false; - } - } - - // Check consistency of colors. - cimglist_for(colors,c) { - const CImg<tc>& color = colors[c]; - if (!color) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) defines no color for primitive [%u]", - _width,primitives._width,c); - return false; - } - } - - // Check consistency of light texture. - if (colors._width>primitives._width) { - const CImg<tc> &light = colors.back(); - if (!light || light._depth>1) { - if (error_message) cimg_sprintf(error_message, - "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)", - _width,primitives._width,light._width, - light._height,light._depth,light._spectrum); - return false; - } - } - - return true; - } - - //! Test if image instance represents a valid serialization of a 3D object. - /** - Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise. - \param full_check Tells if full checking of the instance must be performed. - \param[out] error_message C-string to contain the error message, if the test does not succeed. - \note - - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of - each 3D object component is checked. - - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. - **/ - bool is_CImg3d(const bool full_check=true, char *const error_message=0) const { - if (error_message) *error_message = 0; - - // Check instance dimension and header. - if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { - if (error_message) cimg_sprintf(error_message, - "CImg3d has invalid dimensions (%u,%u,%u,%u)", - _width,_height,_depth,_spectrum); - return false; - } - const T *ptrs = _data, *const ptre = end(); - if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') || - !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) { - if (error_message) cimg_sprintf(error_message, - "CImg3d header not found"); - return false; - } - const unsigned int - nb_points = cimg::float2uint((float)*(ptrs++)), - nb_primitives = cimg::float2uint((float)*(ptrs++)); - - // Check consistency of number of vertices / primitives. - if (!full_check) { - const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives; - if (_data + minimal_size>ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", - nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size); - return false; - } - } - - // Check consistency of vertex data. - if (!nb_points) { - if (nb_primitives) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) defines no vertices but %u primitives", - nb_points,nb_primitives,nb_primitives); - return false; - } - if (ptrs!=ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) is an empty object but contains %u value%s " - "more than expected", - nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); - return false; - } - return true; - } - if (ptrs + 3*nb_points>ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) defines only %u vertices data", - nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); - return false; - } - ptrs+=3*nb_points; - - // Check consistency of primitive data. - if (ptrs==ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) defines %u vertices but no primitive", - nb_points,nb_primitives,nb_points); - return false; - } - - if (!full_check) return true; - - for (unsigned int p = 0; p<nb_primitives; ++p) { - const unsigned int nb_inds = (unsigned int)*(ptrs++); - switch (nb_inds) { - case 1 : { // Point - const unsigned int i0 = cimg::float2uint((float)*(ptrs++)); - if (i0>=nb_points) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive [%u]", - nb_points,nb_primitives,i0,p); - return false; - } - } break; - case 5 : { // Sphere - const unsigned int - i0 = cimg::float2uint((float)*(ptrs++)), - i1 = cimg::float2uint((float)*(ptrs++)); - ptrs+=3; - if (i0>=nb_points || i1>=nb_points) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " - "sphere primitive [%u]", - nb_points,nb_primitives,i0,i1,p); - return false; - } - } break; - case 2 : case 6 : { // Segment - const unsigned int - i0 = cimg::float2uint((float)*(ptrs++)), - i1 = cimg::float2uint((float)*(ptrs++)); - if (nb_inds==6) ptrs+=4; - if (i0>=nb_points || i1>=nb_points) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " - "segment primitive [%u]", - nb_points,nb_primitives,i0,i1,p); - return false; - } - } break; - case 3 : case 9 : { // Triangle - const unsigned int - i0 = cimg::float2uint((float)*(ptrs++)), - i1 = cimg::float2uint((float)*(ptrs++)), - i2 = cimg::float2uint((float)*(ptrs++)); - if (nb_inds==9) ptrs+=6; - if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " - "triangle primitive [%u]", - nb_points,nb_primitives,i0,i1,i2,p); - return false; - } - } break; - case 4 : case 12 : { // Quadrangle - const unsigned int - i0 = cimg::float2uint((float)*(ptrs++)), - i1 = cimg::float2uint((float)*(ptrs++)), - i2 = cimg::float2uint((float)*(ptrs++)), - i3 = cimg::float2uint((float)*(ptrs++)); - if (nb_inds==12) ptrs+=8; - if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " - "quadrangle primitive [%u]", - nb_points,nb_primitives,i0,i1,i2,i3,p); - return false; - } - } break; - default : - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", - nb_points,nb_primitives,p,nb_inds); - return false; - } - if (ptrs>ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " - "%u values missing", - nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); - return false; - } - } - - // Check consistency of color data. - if (ptrs==ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) defines no color/texture data", - nb_points,nb_primitives); - return false; - } - for (unsigned int c = 0; c<nb_primitives; ++c) { - if (*(ptrs++)!=(T)-128) ptrs+=2; - else if ((ptrs+=3)<ptre) { - const unsigned int - w = (unsigned int)*(ptrs - 3), - h = (unsigned int)*(ptrs - 2), - s = (unsigned int)*(ptrs - 1); - if (!h && !s) { - if (w>=c) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u " - "for primitive [%u]", - nb_points,nb_primitives,w,c); - return false; - } - } else ptrs+=w*h*s; - } - if (ptrs>ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " - "%u values missing", - nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); - return false; - } - } - - // Check consistency of opacity data. - if (ptrs==ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) defines no opacity data", - nb_points,nb_primitives); - return false; - } - for (unsigned int o = 0; o<nb_primitives; ++o) { - if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) { - const unsigned int - w = (unsigned int)*(ptrs - 3), - h = (unsigned int)*(ptrs - 2), - s = (unsigned int)*(ptrs - 1); - if (!h && !s) { - if (w>=o) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) refers to invalid shared opacity indice %u " - "for primitive [%u]", - nb_points,nb_primitives,w,o); - return false; - } - } else ptrs+=w*h*s; - } - if (ptrs>ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", - nb_points,nb_primitives,o); - return false; - } - } - - // Check end of data. - if (ptrs<ptre) { - if (error_message) cimg_sprintf(error_message, - "CImg3d (%u,%u) contains %u value%s more than expected", - nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); - return false; - } - return true; - } - - static bool _is_CImg3d(const T val, const char c) { - return val>=(T)c && val<(T)(c + 1); - } - - //@} - //------------------------------------- - // - //! \name Mathematical Functions - //@{ - //------------------------------------- - - // Define the math formula parser/compiler and expression evaluator. - struct _cimg_math_parser { - CImg<doubleT> mem; - CImg<intT> memtype; - CImgList<ulongT> _code, &code, code_begin, code_end; - CImg<ulongT> opcode; - const CImg<ulongT> *p_code_end, *p_code; - const CImg<ulongT> *const p_break; - - CImg<charT> expr, pexpr; - const CImg<T>& imgin; - const CImgList<T>& listin; - CImg<T> &imgout; - CImgList<T>& listout; - - CImg<doubleT> _img_stats, &img_stats, constcache_vals; - CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median; - CImg<uintT> mem_img_stats, constcache_inds; - - CImg<uintT> level, variable_pos, reserved_label; - CImgList<charT> variable_def, macro_def, macro_body; - CImgList<boolT> macro_body_is_string; - char *user_macro; - - unsigned int mempos, mem_img_median, debug_indent, result_dim, break_type, constcache_size; - bool is_parallelizable, is_fill, need_input_copy; - double *result; - ulongT rng; - const char *const calling_function, *s_op, *ss_op; - typedef double (*mp_func)(_cimg_math_parser&); - -#define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value? -#define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value? -#define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value? -#define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable? -#define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? -#define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN) -#define _cimg_mp_calling_function calling_function_s()._data -#define _cimg_mp_op(s) s_op = s; ss_op = ss -#define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char) -#define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char) -#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char) -#define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char) -#define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) -#define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; } -#define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan) -#define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val))) -#define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) -#define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) -#define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2)) -#define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3)) -#define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4)) -#define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5)) -#define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6)) -#define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7)) -#define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1)) -#define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2)) -#define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2)) -#define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2)) -#define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3)) - - // Constructors / Destructors. - ~_cimg_math_parser() { - cimg::srand(rng); - } - - _cimg_math_parser(const char *const expression, const char *const funcname=0, - const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0, - const bool _is_fill=false): - code(_code),p_break((CImg<ulongT>*)0 - 2), - imgin(img_input),listin(list_inputs?*list_inputs:CImgList<T>::const_empty()), - imgout(img_output?*img_output:CImg<T>::empty()),listout(list_outputs?*list_outputs:CImgList<T>::empty()), - img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),user_macro(0), - mem_img_median(~0U),debug_indent(0),result_dim(0),break_type(0),constcache_size(0), - is_parallelizable(true),is_fill(_is_fill),need_input_copy(false), - rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") { -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - if (!expression || !*expression) - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Empty expression.", - pixel_type(),_cimg_mp_calling_function); - const char *_expression = expression; - while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression; - CImg<charT>::string(_expression).move_to(expr); - char *ps = &expr.back() - 1; - while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps; - *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1); - - // Ease the retrieval of previous non-space characters afterwards. - pexpr.assign(expr._width); - char c, *pe = pexpr._data; - for (ps = expr._data, c = ' '; *ps; ++ps) { - if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' '; - *(pe++) = c; - } - *pe = 0; - level = get_level(expr); - - // Init constant values. -#define _cimg_mp_interpolation (reserved_label[29]!=~0U?reserved_label[29]:0) -#define _cimg_mp_boundary (reserved_label[30]!=~0U?reserved_label[30]:0) -#define _cimg_mp_slot_nan 29 -#define _cimg_mp_slot_x 30 -#define _cimg_mp_slot_y 31 -#define _cimg_mp_slot_z 32 -#define _cimg_mp_slot_c 33 - - mem.assign(96); - for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10 - for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5 - mem[16] = 0.5; - mem[17] = 0; // thread_id - mem[18] = (double)imgin._width; // w - mem[19] = (double)imgin._height; // h - mem[20] = (double)imgin._depth; // d - mem[21] = (double)imgin._spectrum; // s - mem[22] = (double)imgin._is_shared; // r - mem[23] = (double)imgin._width*imgin._height; // wh - mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd - mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds - mem[26] = (double)listin._width; // l - mem[27] = std::exp(1.); // e - mem[28] = cimg::PI; // pi - mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan - - // Set value property : - // { -2 = other | -1 = variable | 0 = computation value | - // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }. - memtype.assign(mem._width,1,1,1,0); - for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1; - memtype[17] = 0; - memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -2; - mempos = _cimg_mp_slot_c + 1; - variable_pos.assign(8); - - reserved_label.assign(128,1,1,1,~0U); - // reserved_label[4-28] are used to store these two-char variables: - // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, - // [8] = is, [9] = ip, [10] = ic, [11] = xm, [12] = ym, [13] = zm, [14] = cm, [15] = xM, - // [16] = yM, [17] = zM, [18]=cM, [19]=i0...[28]=i9, [29] = interpolation, [30] = boundary - - // Compile expression into a serie of opcodes. - s_op = ""; ss_op = expr._data; - const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false); - if (!_cimg_mp_is_constant(ind_result)) { - if (_cimg_mp_is_vector(ind_result)) - CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true). - fill(cimg::type<double>::nan()); - else mem[ind_result] = cimg::type<double>::nan(); - } - - // Free resources used for compiling expression and prepare evaluation. - result_dim = _cimg_mp_size(ind_result); - if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1); - result = mem._data + ind_result; - memtype.assign(); - constcache_vals.assign(); - constcache_inds.assign(); - level.assign(); - variable_pos.assign(); - reserved_label.assign(); - expr.assign(); - pexpr.assign(); - opcode.assign(); - opcode._is_shared = true; - - // Execute begin() bloc if any specified. - if (code_begin) { - mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; - p_code_end = code_begin.end(); - for (p_code = code_begin; p_code<p_code_end; ++p_code) { - opcode._data = p_code->_data; - const ulongT target = opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - } - p_code_end = code.end(); - } - - _cimg_math_parser(): - code(_code),p_code_end(0),p_break((CImg<ulongT>*)0 - 2), - imgin(CImg<T>::const_empty()),listin(CImgList<T>::const_empty()), - imgout(CImg<T>::empty()),listout(CImgList<T>::empty()), - img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),debug_indent(0), - result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false),need_input_copy(false), - rng(0),calling_function(0) { - mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() - result = mem._data; - } - - _cimg_math_parser(const _cimg_math_parser& mp): - mem(mp.mem),code(mp.code),p_code_end(mp.p_code_end),p_break(mp.p_break), - imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),img_stats(mp.img_stats), - list_stats(mp.list_stats),list_median(mp.list_median),debug_indent(0),result_dim(mp.result_dim), - break_type(0),constcache_size(0),is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill), - need_input_copy(mp.need_input_copy), result(mem._data + (mp.result - mp.mem._data)), - rng((cimg::_rand(),cimg::rng())),calling_function(0) { -#ifdef cimg_use_openmp - mem[17] = omp_get_thread_num(); - rng+=omp_get_thread_num(); -#endif - opcode.assign(); - opcode._is_shared = true; - } - - // Count parentheses/brackets level of each character of the expression. - CImg<uintT> get_level(CImg<charT>& expr) const { - bool is_escaped = false, next_is_escaped = false; - unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string - CImg<uintT> res(expr._width - 1); - unsigned int *pd = res._data; - int level = 0; - for (const char *ps = expr._data; *ps && level>=0; ++ps) { - if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true; - if (!is_escaped && *ps=='\'') { // Non-escaped character - if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string - else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string - else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string - } - *(pd++) = (unsigned int)(mode>=1 || is_escaped?level + (mode==1): - *ps=='(' || *ps=='['?level++: - *ps==')' || *ps==']'?--level: - level); - mode = next_mode; - is_escaped = next_is_escaped; - next_is_escaped = false; - } - if (mode) { - cimg::strellipsize(expr,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Unterminated string literal, in expression '%s'.", - pixel_type(),_cimg_mp_calling_function, - expr._data); - } - if (level) { - cimg::strellipsize(expr,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", - pixel_type(),_cimg_mp_calling_function, - expr._data); - } - return res; - } - - // Tell for each character of an expression if it is inside a string or not. - CImg<boolT> is_inside_string(CImg<charT>& expr) const { - bool is_escaped = false, next_is_escaped = false; - unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string - CImg<boolT> res = CImg<charT>::string(expr); - bool *pd = res._data; - for (const char *ps = expr._data; *ps; ++ps) { - if (!next_is_escaped && *ps=='\\') next_is_escaped = true; - if (!is_escaped && *ps=='\'') { // Non-escaped character - if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string - else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string - else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string - } - *(pd++) = mode>=1 || is_escaped; - mode = next_mode; - is_escaped = next_is_escaped; - next_is_escaped = false; - } - return res; - } - - // Compilation procedure. - unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref, - const bool is_single) { - if (depth>256) { - cimg::strellipsize(expr,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Call stack overflow (infinite recursion?), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); - } - char c1, c2, c3, c4; - - // Simplify expression when possible. - do { - c2 = 0; - if (ss<se) { - while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss; - while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; - } - while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { - ++ss; --se; c2 = 1; - } - } while (c2 && ss<se); - - if (se<=ss || !*ss) { - cimg::strellipsize(expr,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s%s Missing %s, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - *s_op=='F'?"argument":"item", - (ss_op - 4)>expr._data?"...":"", - (ss_op - 4)>expr._data?ss_op - 4:expr._data, - ss_op + std::strlen(ss_op)<&expr.back()?"...":""); - } - - const char *const previous_s_op = s_op, *const previous_ss_op = ss_op; - const unsigned int depth1 = depth + 1; - unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6; - char - *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, - *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4, - *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8, - *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0; - double val = 0, val1, val2; - mp_func op; - - // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value - // linked to the returned memory slot (reference that cannot be determined at compile time). - // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) | - // 3 = image value (coordinates) | 4 = image value as a vector (offsets) | - // 5 = image value as a vector (coordinates) }. - // Depending on p_ref[0], the remaining p_ref[k] have the following meaning: - // When p_ref[0]==0, p_ref is actually unlinked. - // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ]. - // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ]. - // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ]. - // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ]. - // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ]. - if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; } - - const char saved_char = *se; *se = 0; - const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; - bool is_sth, is_relative; - CImg<uintT> ref; - CImg<charT> variable_name; - CImgList<ulongT> l_opcode; - - // Look for a single value or a pre-defined variable. - int nb = 0; - s = ss + (*ss=='+' || *ss=='-'?1:0); - if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf - is_sth = !(*ss=='-'); - if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; } - else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; } - if (nb==1 && !is_sth) val = -val; - } - if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); - if (nb==1) _cimg_mp_constant(val); - if (nb==2 && sep=='%') _cimg_mp_constant(val/100); - - if (ss1==se) switch (*ss) { // One-char reserved variable - case 'c' : _cimg_mp_return(reserved_label['c']!=~0U?reserved_label['c']:_cimg_mp_slot_c); - case 'd' : _cimg_mp_return(reserved_label['d']!=~0U?reserved_label['d']:20); - case 'e' : _cimg_mp_return(reserved_label['e']!=~0U?reserved_label['e']:27); - case 'h' : _cimg_mp_return(reserved_label['h']!=~0U?reserved_label['h']:19); - case 'l' : _cimg_mp_return(reserved_label['l']!=~0U?reserved_label['l']:26); - case 'r' : _cimg_mp_return(reserved_label['r']!=~0U?reserved_label['r']:22); - case 's' : _cimg_mp_return(reserved_label['s']!=~0U?reserved_label['s']:21); - case 't' : _cimg_mp_return(reserved_label['t']!=~0U?reserved_label['t']:17); - case 'w' : _cimg_mp_return(reserved_label['w']!=~0U?reserved_label['w']:18); - case 'x' : _cimg_mp_return(reserved_label['x']!=~0U?reserved_label['x']:_cimg_mp_slot_x); - case 'y' : _cimg_mp_return(reserved_label['y']!=~0U?reserved_label['y']:_cimg_mp_slot_y); - case 'z' : _cimg_mp_return(reserved_label['z']!=~0U?reserved_label['z']:_cimg_mp_slot_z); - case 'u' : - if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']); - _cimg_mp_scalar2(mp_u,0,1); - case 'g' : - if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']); - _cimg_mp_scalar0(mp_g); - case 'i' : - if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']); - _cimg_mp_scalar0(mp_i); - case 'I' : - _cimg_mp_op("Variable 'I'"); - if (reserved_label['I']!=~0U) _cimg_mp_return(reserved_label['I']); - if (!imgin._spectrum) _cimg_mp_return(0); - need_input_copy = true; - pos = vector(imgin._spectrum); - CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code); - _cimg_mp_return(pos); - case 'R' : - if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0); - case 'G' : - if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0); - case 'B' : - if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0); - case 'A' : - if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0); - } - else if (ss2==se) { // Two-chars reserved variable - arg1 = arg2 = ~0U; - if (*ss=='w' && *ss1=='h') // wh - _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23); - if (*ss=='p' && *ss1=='i') // pi - _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28); - if (*ss=='i') { - if (*ss1>='0' && *ss1<='9') { // i0...i9 - pos = 19 + *ss1 - '0'; - if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); - need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 19,0,0); - } - switch (*ss1) { - case 'm' : arg1 = 4; arg2 = 0; break; // im - case 'M' : arg1 = 5; arg2 = 1; break; // iM - case 'a' : arg1 = 6; arg2 = 2; break; // ia - case 'v' : arg1 = 7; arg2 = 3; break; // iv - case 's' : arg1 = 8; arg2 = 12; break; // is - case 'p' : arg1 = 9; arg2 = 13; break; // ip - case 'c' : // ic - if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); - if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; - _cimg_mp_return(mem_img_median); - break; - } - } - else if (*ss1=='m') switch (*ss) { - case 'x' : arg1 = 11; arg2 = 4; break; // xm - case 'y' : arg1 = 12; arg2 = 5; break; // ym - case 'z' : arg1 = 13; arg2 = 6; break; // zm - case 'c' : arg1 = 14; arg2 = 7; break; // cm - } - else if (*ss1=='M') switch (*ss) { - case 'x' : arg1 = 15; arg2 = 8; break; // xM - case 'y' : arg1 = 16; arg2 = 9; break; // yM - case 'z' : arg1 = 17; arg2 = 10; break; // zM - case 'c' : arg1 = 18; arg2 = 11; break; // cM - } - if (arg1!=~0U) { - if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); - if (!img_stats) { - img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false); - mem_img_stats.assign(1,14,1,1,~0U); - } - if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]); - _cimg_mp_return(mem_img_stats[arg2]); - } - } else if (ss3==se) { // Three-chars reserved variable - if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd - _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24); - } else if (ss4==se) { // Four-chars reserved variable - if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds - _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25); - } - - pos = ~0U; - is_sth = false; - for (s0 = ss, s = ss1; s<se1; ++s) - if (*s==';' && level[s - expr._data]==clevel) { // Separator ';' - arg1 = code_end._width; - arg2 = compile(s0,s++,depth,0,is_single); - if (code_end._width==arg1) pos = arg2; // makes 'end()' return void - is_sth = true; - while (*s && (cimg::is_blank(*s) || *s==';')) ++s; - s0 = s; - } - if (is_sth) { - arg1 = code_end._width; - arg2 = compile(s0,se,depth,p_ref,is_single); - if (code_end._width==arg1) pos = arg2; // makes 'end()' return void - _cimg_mp_return(pos); - } - - // Declare / assign variable, vector value or image value. - for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns) - if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' && - *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' && - *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' && - level[s - expr._data]==clevel) { - variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0; - cimg::strpare(variable_name,false,true); - const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name); - char *const ve1 = ss + l_variable_name - 1; - _cimg_mp_op("Operator '='"); - - // Assign image value (direct). - if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') && - (reserved_label[*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[*ss]))) { - is_relative = *ss=='j' || *ss=='J'; - - if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value - if (!is_single) is_parallelizable = false; - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss3,s0++,depth1,0,is_single); - _cimg_mp_check_list(true); - } else { p1 = ~0U; s0 = ss2; } - arg1 = compile(s0,ve1,depth1,0,is_single); // Offset - _cimg_mp_check_type(arg1,0,1,0); - arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign - if (_cimg_mp_is_vector(arg2)) { - p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any - if (p1==~0U) p2 = imgin._spectrum; - else if (_cimg_mp_is_constant(p1)) { - p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - p2 = listin[p3]._spectrum; - } - if (!p2) _cimg_mp_return(0); - } else p2 = 0; - _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,p2); - - if (p_ref) { - *p_ref = _cimg_mp_is_vector(arg2)?4:2; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - if (_cimg_mp_is_vector(arg2)) - set_variable_vector(arg2); // Prevent from being used in further optimization - else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; - if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; - if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2; - } - - - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - if (*ss>='i') - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg2,p1,arg1).move_to(code); - else if (_cimg_mp_is_scalar(arg2)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), - arg2,p1,arg1).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - if (*ss>='i') - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), - arg2,arg1).move_to(code); - else if (_cimg_mp_is_scalar(arg2)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), - arg2,arg1).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg2,arg1,_cimg_mp_size(arg2)).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value - if (!is_single) is_parallelizable = false; - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss3,s0++,depth1,0,is_single); - _cimg_mp_check_list(true); - } else { p1 = ~0U; s0 = ss2; } - arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x; - arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y; - arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z; - arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c; - arg5 = compile(s + 1,se,depth1,0,is_single); // Value to assign - if (s0<ve1) { // X or [ X,_Y,_Z,_C ] - s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(s0,s1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector - p2 = _cimg_mp_size(arg1); // Vector size - ++arg1; - if (p2>1) { - arg2 = arg1 + 1; - if (p2>2) { - arg3 = arg2 + 1; - if (p2>3) arg4 = arg3 + 1; - } - } - } else if (s1<ve1) { // Y - s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(s1,s2,depth1,0,is_single); - if (s2<ve1) { // Z - s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg3 = compile(s2,s3,depth1,0,is_single); - if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,is_single); // C - } - } - } - - if (_cimg_mp_is_vector(arg5)) { - p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any - if (p1==~0U) p2 = imgin._spectrum; - else if (_cimg_mp_is_constant(p1)) { - p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - p2 = listin[p3]._spectrum; - } - if (!p2) _cimg_mp_return(0); - } else p2 = 0; - _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,p2); - - if (p_ref) { - *p_ref = _cimg_mp_is_vector(arg5)?5:3; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - p_ref[4] = arg2; - p_ref[5] = arg3; - p_ref[6] = arg4; - if (_cimg_mp_is_vector(arg5)) - set_variable_vector(arg5); // Prevent from being used in further optimization - else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -2; - if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; - if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2; - if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; - if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; - if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2; - } - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg5); - if (*ss>='i') - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg5,p1,arg1,arg2,arg3,arg4).move_to(code); - else if (_cimg_mp_is_scalar(arg5)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), - arg5,p1,arg1,arg2,arg3).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg5); - if (*ss>='i') - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg5,arg1,arg2,arg3,arg4).move_to(code); - else if (_cimg_mp_is_scalar(arg5)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), - arg5,arg1,arg2,arg3).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); - } - _cimg_mp_return(arg5); - } - } - - // Assign vector value (direct). - if (l_variable_name>3 && *ve1==']' && *ss!='[') { - s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; - is_sth = true; // is_valid_variable_name? - if (*ss>='0' && *ss<='9') is_sth = false; - else for (ns = ss; ns<s0; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - if (is_sth && s0>ss) { - variable_name[s0 - ss] = 0; // Remove brackets in variable name - arg1 = ~0U; // Vector slot - arg2 = compile(++s0,ve1,depth1,0,is_single); // Index - arg3 = compile(s + 1,se,depth1,0,is_single); // Value to assign - _cimg_mp_check_type(arg3,2,1,0); - - if (variable_name[1]) { // Multi-char variable - cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i])) { - arg1 = variable_pos[i]; break; - } - } else arg1 = reserved_label[*variable_name]; // Single-char variable - if (arg1==~0U) compile(ss,s0 - 1,depth1,0,is_single); // Variable does not exist -> error - else { // Variable already exists - if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0,is_single); // Variable is not a vector -> error - if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly - nb = (int)mem[arg2]; - if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) { - arg1+=nb + 1; - CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code); - _cimg_mp_return(arg1); - } - compile(ss,s,depth1,0,is_single); // Out-of-bounds reference -> error - } - - // Case of non-constant index -> return assigned value + linked reference - if (p_ref) { - *p_ref = 1; - p_ref[1] = arg1; - p_ref[2] = arg2; - if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; // Prevent from being used in further optimization - if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; - } - CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1), - arg2,arg3). - move_to(code); - _cimg_mp_return(arg3); - } - } - } - - // Assign user-defined macro. - if (l_variable_name>2 && *ve1==')' && *ss!='(') { - s0 = ve1; while (s0>ss && *s0!='(') --s0; - is_sth = std::strncmp(variable_name,"debug(",6) && - std::strncmp(variable_name,"print(",6); // is_valid_function_name? - if (*ss>='0' && *ss<='9') is_sth = false; - else for (ns = ss; ns<s0; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - - if (is_sth && s0>ss) { // Looks like a valid function declaration - s0 = variable_name._data + (s0 - ss); - *s0 = 0; - s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis - CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0); - ++s; while (*s && cimg::is_blank(*s)) ++s; - CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0); - - p1 = 1; // Indice of current parsed argument - for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments - if (p1>24) { - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro " - "definition '%s()', in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - while (*s && cimg::is_blank(*s)) ++s; - if (*s==')' && p1==1) break; // Function has no arguments - - s2 = s; // Start of the argument name - is_sth = true; // is_valid_argument_name? - if (*s>='0' && *s<='9') is_sth = false; - else for (ns = s; ns<s1 && *ns!=',' && !cimg::is_blank(*ns); ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - s3 = ns; // End of the argument name - while (*ns && cimg::is_blank(*ns)) ++ns; - if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) { - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: %s name specified for argument %u when defining " - "macro '%s()', in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - is_sth?"Empty":"Invalid",p1, - variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - if (ns==s1 || *ns==',') { // New argument found - *s3 = 0; - p2 = (unsigned int)(s3 - s2); // Argument length - for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number - if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) || - (ps + p2<macro_body[0].end() && is_varchar(*(ps + p2))))) { - if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign - *(ps - 1) = (char)p1; - if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs - std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1); - macro_body[0]._width-=p2 + 1; - } else { // Has pre number sign only - std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2); - macro_body[0]._width-=p2; - } - } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign - *(ps++) = (char)p1; - std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2); - macro_body[0]._width-=p2; - } else { // Not near a number sign - if (p2<3) { - ps-=(ulongT)macro_body[0]._data; - macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0); - ps+=(ulongT)macro_body[0]._data; - } else macro_body[0]._width-=p2 - 3; - std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3); - *(ps++) = '('; - *(ps++) = (char)p1; - *(ps++) = ')'; - } - } else ++ps; - } - } - } - - // Store number of arguments. - macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1); - - // Detect parts of function body inside a string. - is_inside_string(macro_body[0]).move_to(macro_body_is_string,0); - _cimg_mp_return_nan(); - } - } - - // Check if the variable name could be valid. If not, this is probably an lvalue assignment. - is_sth = true; // is_valid_variable_name? - const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6); - - s0 = variable_name._data; - if (is_const) { - s0+=6; while (cimg::is_blank(*s0)) ++s0; - variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1); - } - - if (*variable_name>='0' && *variable_name<='9') is_sth = false; - else for (ns = variable_name._data; *ns; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - - // Assign variable (direct). - if (is_sth) { - arg3 = variable_name[1]?~0U:*variable_name; // One-char variable - if (variable_name[1] && !variable_name[2]) { // Two-chars variable - c1 = variable_name[0]; - c2 = variable_name[1]; - if (c1=='w' && c2=='h') arg3 = 0; // wh - else if (c1=='p' && c2=='i') arg3 = 3; // pi - else if (c1=='i') { - if (c2>='0' && c2<='9') arg3 = 19 + c2 - '0'; // i0...i9 - else if (c2=='m') arg3 = 4; // im - else if (c2=='M') arg3 = 5; // iM - else if (c2=='a') arg3 = 6; // ia - else if (c2=='v') arg3 = 7; // iv - else if (c2=='s') arg3 = 8; // is - else if (c2=='p') arg3 = 9; // ip - else if (c2=='c') arg3 = 10; // ic - } else if (c2=='m') { - if (c1=='x') arg3 = 11; // xm - else if (c1=='y') arg3 = 12; // ym - else if (c1=='z') arg3 = 13; // zm - else if (c1=='c') arg3 = 14; // cm - } else if (c2=='M') { - if (c1=='x') arg3 = 15; // xM - else if (c1=='y') arg3 = 16; // yM - else if (c1=='z') arg3 = 17; // zM - else if (c1=='c') arg3 = 18; // cM - } - } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable - c1 = variable_name[0]; - c2 = variable_name[1]; - c3 = variable_name[2]; - if (c1=='w' && c2=='h' && c3=='d') arg3 = 1; // whd - } else if (variable_name[1] && variable_name[2] && variable_name[3] && - !variable_name[4]) { // Four-chars variable - c1 = variable_name[0]; - c2 = variable_name[1]; - c3 = variable_name[2]; - c4 = variable_name[3]; - if (c1=='w' && c2=='h' && c3=='d' && c4=='s') arg3 = 2; // whds - } else if (!std::strcmp(variable_name,"interpolation")) arg3 = 29; // interpolation - else if (!std::strcmp(variable_name,"boundary")) arg3 = 30; // boundary - - arg1 = ~0U; - arg2 = compile(s + 1,se,depth1,0,is_single); - if (is_const) _cimg_mp_check_constant(arg2,2,0); - - if (arg3!=~0U) // One-char variable, or variable in reserved_labels - arg1 = reserved_label[arg3]; - else // Multi-char variable name : check for existing variable with same name - cimglist_for(variable_def,i) - if (!std::strcmp(variable_name,variable_def[i])) { arg1 = variable_pos[i]; break; } - - if (arg1==~0U) { // Create new variable - if (_cimg_mp_is_vector(arg2)) { // Vector variable - arg1 = is_comp_vector(arg2)?arg2:vector_copy(arg2); - set_variable_vector(arg1); - } else { // Scalar variable - if (is_const) arg1 = arg2; - else { - arg1 = _cimg_mp_is_comp(arg2)?arg2:scalar1(mp_copy,arg2); - memtype[arg1] = -1; - } - } - - if (arg3!=~0U) reserved_label[arg3] = arg1; - else { - if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); - variable_pos[variable_def._width] = arg1; - variable_name.move_to(variable_def); - } - - } else { // Variable already exists -> assign a new value - if (is_const || _cimg_mp_is_constant(arg1)) { - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - _cimg_mp_is_constant(arg1)?"already-defined ":"non-", - variable_name._data, - !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"", - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - _cimg_mp_check_type(arg2,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1)) { // Vector - if (_cimg_mp_is_vector(arg2)) // From vector - CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)). - move_to(code); - else // From scalar - CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2). - move_to(code); - } else // Scalar - CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code); - } - _cimg_mp_return(arg1); - } - - // Assign lvalue (variable name was not valid for a direct assignment). - arg1 = ~0U; - is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? - if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment - - if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { - ref.assign(7); - arg1 = compile(ss,s,depth1,ref,is_single); // Lvalue slot - arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign - - if (*ref==1) { // Vector value (scalar): V[k] = scalar - _cimg_mp_check_type(arg2,2,1,0); - arg3 = ref[1]; // Vector slot - arg4 = ref[2]; // Index - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg2). - move_to(code); - _cimg_mp_return(arg2); - } - - if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,1,0); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg2,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), - arg2,arg3).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,1,0); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - arg6 = ref[6]; // C - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg2,p1,arg3,arg4,arg5,arg6).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg2,arg3,arg4,arg5,arg6).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), - arg2,p1,arg3).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), - arg2,arg3).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg2,arg3,_cimg_mp_size(arg2)).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), - arg2,p1,arg3,arg4,arg5).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg2); - if (_cimg_mp_is_scalar(arg2)) - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), - arg2,arg3,arg4,arg5).move_to(code); - else - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); - } - _cimg_mp_return(arg2); - } - - if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg2)) // From vector - CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)). - move_to(code); - else // From scalar - CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2). - move_to(code); - _cimg_mp_return(arg1); - } - - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar - _cimg_mp_check_type(arg2,2,1,0); - CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code); - _cimg_mp_return(arg1); - } - } - - // No assignment expressions match -> error - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Invalid %slvalue '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"", - variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - - // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++. - for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 - if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps && - level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=) - _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"); - - ref.assign(7); - arg1 = compile(ss,ns,depth1,ref,is_single); // Vector slot - arg2 = compile(s + 1,se,depth1,0,is_single); // Right operand - _cimg_mp_check_type(arg1,1,2,2); - _cimg_mp_check_type(arg2,2,3,2); - if (_cimg_mp_is_vector(arg2)) { // Complex **= complex - if (*ps=='*') - CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code); - else if (*ps=='/') - CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code); - else - CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code); - } else { // Complex **= scalar - if (*ps=='*') { - if (arg2==1) _cimg_mp_return(arg1); - self_vector_s(arg1,mp_self_mul,arg2); - } else if (*ps=='/') { - if (arg2==1) _cimg_mp_return(arg1); - self_vector_s(arg1,mp_self_div,arg2); - } else { - if (arg2==1) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code); - } - } - - // Write computed value back in image if necessary. - if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value - if (!is_single) is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3,_cimg_mp_size(arg1)).move_to(code); - } - - } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value - if (!is_single) is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); - } - } - - _cimg_mp_return(arg1); - } - - for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 - if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' || - *ps=='&' || *ps=='^' || *ps=='|' || - (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && - level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=) - switch (*ps) { - case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break; - case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break; - case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break; - case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break; - case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break; - case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break; - case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break; - case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break; - case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break; - default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break; - } - s1 = *ps=='>' || *ps=='<'?ns:ps; - - ref.assign(7); - arg1 = compile(ss,s1,depth1,ref,is_single); // Variable slot - arg2 = compile(s + 1,se,depth1,0,is_single); // Value to apply - - // Check for particular case to be simplified. - if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1); - if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1); - - // Apply operator on a copy to prevent modifying a constant or a variable. - if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) { - if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); - else arg1 = scalar1(mp_copy,arg1); - } - - if (*ref==1) { // Vector value (scalar): V[k] += scalar - _cimg_mp_check_type(arg2,2,1,0); - arg3 = ref[1]; // Vector slot - arg4 = ref[2]; // Index - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code); - CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1). - move_to(code); - _cimg_mp_return(arg1); - } - - if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,1,0); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg1,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), - arg1,arg3).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,1,0); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - arg6 = ref[6]; // C - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg1,p1,arg3,arg4,arg5,arg6).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg1,arg3,arg4,arg5,arg6).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3,_cimg_mp_size(arg1)).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value - if (!is_single) is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(arg1); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); - } - _cimg_mp_return(arg1); - } - - if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector - else self_vector_s(arg1,op,arg2); // Vector += scalar - _cimg_mp_return(arg1); - } - - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar - _cimg_mp_check_type(arg2,2,1,0); - CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code); - _cimg_mp_return(arg1); - } - - variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0; - cimg::strpare(variable_name,false,true); - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Invalid %slvalue '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - _cimg_mp_is_constant(arg1)?"const ":"", - variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - - for (s = ss1; s<se1; ++s) - if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2' - _cimg_mp_op("Operator '?:'"); - s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1; - arg1 = compile(ss,s,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - if (_cimg_mp_is_constant(arg1)) { - if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single); - else return *s1!=':'?0:compile(++s1,se,depth1,0,is_single); - } - p2 = code._width; - arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single); - p3 = code._width; - arg3 = *s1==':'?compile(++s1,se,depth1,0,is_single): - _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0; - _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2)); - arg4 = _cimg_mp_size(arg2); - if (arg4) pos = vector(arg4); else pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3, - p3 - p2,code._width - p3,arg4).move_to(code,p2); - _cimg_mp_return(pos); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') - _cimg_mp_op("Operator '||'"); - arg1 = compile(ss,s,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - if (arg1>0 && arg1<=16) _cimg_mp_return(1); - p2 = code._width; - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(mem[arg1] || mem[arg2]); - if (!arg1) _cimg_mp_return(arg2); - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2). - move_to(code,p2); - _cimg_mp_return(pos); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') - _cimg_mp_op("Operator '&&'"); - arg1 = compile(ss,s,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - if (!arg1) _cimg_mp_return(0); - p2 = code._width; - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(mem[arg1] && mem[arg2]); - if (arg1>0 && arg1<=16) _cimg_mp_return(arg2); - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2). - move_to(code,p2); - _cimg_mp_return(pos); - } - - for (s = se2; s>ss; --s) - if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') - _cimg_mp_op("Operator '|'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { - if (!arg2) _cimg_mp_return(arg1); - _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2); - } - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { - if (!arg1) _cimg_mp_return(arg2); - _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2); - } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]); - if (!arg2) _cimg_mp_return(arg1); - if (!arg1) _cimg_mp_return(arg2); - _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2); - } - - for (s = se2; s>ss; --s) - if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') - _cimg_mp_op("Operator '&'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]); - if (!arg1 || !arg2) _cimg_mp_return(0); - _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') - _cimg_mp_op("Operator '!='"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - if (arg1==arg2) _cimg_mp_return(0); - p1 = _cimg_mp_size(arg1); - p2 = _cimg_mp_size(arg2); - if (p1 || p2) { - if (p1 && p2 && p1!=p2) _cimg_mp_return(1); - _cimg_mp_scalar6(mp_vector_neq,arg1,p1,arg2,p2,11,1); - } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]); - _cimg_mp_scalar2(mp_neq,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') - _cimg_mp_op("Operator '=='"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - if (arg1==arg2) _cimg_mp_return(1); - p1 = _cimg_mp_size(arg1); - p2 = _cimg_mp_size(arg2); - if (p1 || p2) { - if (p1 && p2 && p1!=p2) _cimg_mp_return(0); - _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,11,1); - } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]); - _cimg_mp_scalar2(mp_eq,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') - _cimg_mp_op("Operator '<='"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]); - if (arg1==arg2) _cimg_mp_return(1); - _cimg_mp_scalar2(mp_lte,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') - _cimg_mp_op("Operator '>='"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]); - if (arg1==arg2) _cimg_mp_return(1); - _cimg_mp_scalar2(mp_gte,arg1,arg2); - } - - for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) - if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') - _cimg_mp_op("Operator '<'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<mem[arg2]); - if (arg1==arg2) _cimg_mp_return(0); - _cimg_mp_scalar2(mp_lt,arg1,arg2); - } - - for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) - if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than ('>') - _cimg_mp_op("Operator '>'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]); - if (arg1==arg2) _cimg_mp_return(0); - _cimg_mp_scalar2(mp_gt,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') - _cimg_mp_op("Operator '<<'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { - if (!arg2) _cimg_mp_return(arg1); - _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2); - } - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]); - if (!arg1) _cimg_mp_return(0); - if (!arg2) _cimg_mp_return(arg1); - _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') - _cimg_mp_op("Operator '>>'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { - if (!arg2) _cimg_mp_return(arg1); - _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2); - } - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) - _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]); - if (!arg1) _cimg_mp_return(0); - if (!arg2) _cimg_mp_return(arg1); - _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2); - } - - for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) - if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && - *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && - (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && - *(ps - 1)<='9')))) && - level[s - expr._data]==clevel) { // Addition ('+') - _cimg_mp_op("Operator '+'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (!arg2) _cimg_mp_return(arg1); - if (!arg1) _cimg_mp_return(arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]); - if (code) { // Try to spot linear case 'a*b + c' - CImg<ulongT> &pop = code.back(); - if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { - arg3 = (unsigned int)pop[1]; - arg4 = (unsigned int)pop[2]; - arg5 = (unsigned int)pop[3]; - code.remove(); - CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); - _cimg_mp_return(arg3); - } - } - if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1); - if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2); - _cimg_mp_scalar2(mp_add,arg1,arg2); - } - - for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) - if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && - *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && - (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && - *(ps - 1)<='9')))) && - level[s - expr._data]==clevel) { // Subtraction ('-') - _cimg_mp_op("Operator '-'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (!arg2) _cimg_mp_return(arg1); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { - if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2); - _cimg_mp_vector2_sv(mp_sub,arg1,arg2); - } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]); - if (!arg1) _cimg_mp_scalar1(mp_minus,arg2); - if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b' - CImg<ulongT> &pop = code.back(); - if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { - arg3 = (unsigned int)pop[1]; - arg4 = (unsigned int)pop[2]; - arg5 = (unsigned int)pop[3]; - code.remove(); - CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right), - arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code); - _cimg_mp_return(arg3); - } - } - if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1); - _cimg_mp_scalar2(mp_sub,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**') - _cimg_mp_op("Operator '**'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,3,2); - _cimg_mp_check_type(arg2,2,3,2); - if (arg2==1) _cimg_mp_return(arg1); - if (arg1==1) _cimg_mp_return(arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { - pos = vector(2); - CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); - if (!arg1 || !arg2) _cimg_mp_return(0); - _cimg_mp_scalar2(mp_mul,arg1,arg2); - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') - _cimg_mp_op("Operator '//'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,3,2); - _cimg_mp_check_type(arg2,2,3,2); - if (arg2==1) _cimg_mp_return(arg1); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { - pos = vector(2); - CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { - pos = vector(2); - CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); - if (!arg1) _cimg_mp_return(0); - _cimg_mp_scalar2(mp_div,arg1,arg2); - } - - for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') - _cimg_mp_op("Operator '*'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - p2 = _cimg_mp_size(arg2); - if (p2>0 && _cimg_mp_size(arg1)==p2*p2) { // Particular case of matrix multiplication - pos = vector(p2); - CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code); - _cimg_mp_return(pos); - } - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (arg2==1) _cimg_mp_return(arg1); - if (arg1==1) _cimg_mp_return(arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); - - if (code) { // Try to spot double multiplication 'a*b*c' - CImg<ulongT> &pop = code.back(); - if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { - arg3 = (unsigned int)pop[1]; - arg4 = (unsigned int)pop[2]; - arg5 = (unsigned int)pop[3]; - code.remove(); - CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); - _cimg_mp_return(arg3); - } - } - if (!arg1 || !arg2) _cimg_mp_return(0); - _cimg_mp_scalar2(mp_mul,arg1,arg2); - } - - for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') - _cimg_mp_op("Operator '/'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (arg2==1) _cimg_mp_return(arg1); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); - if (!arg1) _cimg_mp_return(0); - _cimg_mp_scalar2(mp_div,arg1,arg2); - } - - for (s = se2, ns = se1; s>ss; --s, --ns) - if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') - _cimg_mp_op("Operator '%'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); - _cimg_mp_scalar2(mp_modulo,arg1,arg2); - } - - if (se1>ss) { - if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+') - _cimg_mp_op("Operator '+'"); - _cimg_mp_return(compile(ss1,se,depth1,0,is_single)); - } - - if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-') - _cimg_mp_op("Operator '-'"); - arg1 = compile(ss1,se,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); - _cimg_mp_scalar1(mp_minus,arg1); - } - - if (*ss=='!') { // Logical not ('!') - _cimg_mp_op("Operator '!'"); - if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)' - arg1 = compile(ss2,se,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); - _cimg_mp_scalar1(mp_bool,arg1); - } - arg1 = compile(ss1,se,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); - _cimg_mp_scalar1(mp_logical_not,arg1); - } - - if (*ss=='~') { // Bitwise not ('~') - _cimg_mp_op("Operator '~'"); - arg1 = compile(ss1,se,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]); - _cimg_mp_scalar1(mp_bitwise_not,arg1); - } - } - - for (s = se3, ns = se2; s>ss; --s, --ns) - if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') - _cimg_mp_op("Operator '^^'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 2,se,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,3,2); - _cimg_mp_check_type(arg2,2,3,2); - if (arg2==1) _cimg_mp_return(arg1); - pos = vector(2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { - CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { - CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { - CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - - for (s = se2; s>ss; --s) - if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') - _cimg_mp_op("Operator '^'"); - arg1 = compile(ss,s,depth1,0,is_single); - arg2 = compile(s + 1,se,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (arg2==1) _cimg_mp_return(arg1); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); - switch (arg2) { - case 0 : _cimg_mp_return(1); - case 2 : _cimg_mp_scalar1(mp_sqr,arg1); - case 3 : _cimg_mp_scalar1(mp_pow3,arg1); - case 4 : _cimg_mp_scalar1(mp_pow4,arg1); - default : - if (_cimg_mp_is_constant(arg2)) { - if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); } - else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); } - } - _cimg_mp_scalar2(mp_pow,arg1,arg2); - } - } - - // Percentage computation. - if (*se1=='%') { - arg1 = compile(ss,se1,depth1,0,is_single); - arg2 = _cimg_mp_is_constant(arg1)?0:constant(100); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100); - _cimg_mp_scalar2(mp_div,arg1,arg2); - } - - is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-? - if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment - if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) { - _cimg_mp_op("Operator '++'"); - op = mp_self_increment; - } else { - _cimg_mp_op("Operator '--'"); - op = mp_self_decrement; - } - ref.assign(7); - arg1 = is_sth?compile(ss2,se,depth1,ref,is_single): - compile(ss,se2,depth1,ref,is_single); // Variable slot - - // Apply operator on a copy to prevent modifying a constant or a variable. - if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) { - if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); - else arg1 = scalar1(mp_copy,arg1); - } - - if (is_sth) pos = arg1; // Determine return indice, depending on pre/post action - else { - if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1); - else pos = scalar1(mp_copy,arg1); - } - - if (*ref==1) { // Vector value (scalar): V[k]++ - arg3 = ref[1]; // Vector slot - arg4 = ref[2]; // Index - if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code); - CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1). - move_to(code); - _cimg_mp_return(pos); - } - - if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ - if (!is_single) is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg<ulongT>::vector((ulongT)op,arg1).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), - arg1,p1,arg3).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), - arg1,arg3).move_to(code); - } - _cimg_mp_return(pos); - } - - if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ - if (!is_single) is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - arg6 = ref[6]; // C - if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg<ulongT>::vector((ulongT)op,arg1).move_to(code); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), - arg1,p1,arg3,arg4,arg5,arg6).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), - arg1,arg3,arg4,arg5,arg6).move_to(code); - } - _cimg_mp_return(pos); - } - - if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ - if (!is_single) is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // Offset - if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3,_cimg_mp_size(arg1)).move_to(code); - } - _cimg_mp_return(pos); - } - - if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ - if (!is_single) is_parallelizable = false; - p1 = ref[1]; // Index - is_relative = (bool)ref[2]; - arg3 = ref[3]; // X - arg4 = ref[4]; // Y - arg5 = ref[5]; // Z - if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); - if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); - } else { - if (!imgout) _cimg_mp_return(pos); - CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); - } - _cimg_mp_return(pos); - } - - if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++ - self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); - _cimg_mp_return(pos); - } - - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++ - CImg<ulongT>::vector((ulongT)op,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1)); - else variable_name.assign(ss,(unsigned int)(se1 - ss)); - variable_name.back() = 0; - cimg::strpare(variable_name,false,true); - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Invalid %slvalue '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - _cimg_mp_is_constant(arg1)?"const ":"", - variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - - // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'. - if (*se1==']' && *ss!='[') { - _cimg_mp_op("Value accessor '[]'"); - is_relative = *ss=='j' || *ss=='J'; - s0 = s1 = std::strchr(ss,'['); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } - - if ((*ss=='I' || *ss=='J') && *ss1=='[' && - (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a vector - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss3,s0++,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { p1 = ~0U; s0 = ss2; } - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - p2 = 1 + (p1!=~0U); - arg1 = compile(s0,s1,depth1,0,is_single); // Offset - _cimg_mp_check_type(arg1,p2,1,0); - arg2 = ~0U; - if (s1<se1) { - arg2 = compile(++s1,se1,depth1,0,is_single); // Boundary - _cimg_mp_check_type(arg2,p2 + 1,1,0); - } - if (p_ref && arg2==~0U) { - *p_ref = 4; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization - if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2; - } - p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any - if (p1==~0U) p2 = imgin._spectrum; - else if (_cimg_mp_is_constant(p1)) { - p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - p2 = listin[p3]._spectrum; - } - if (!p2) _cimg_mp_return(0); - pos = vector(p2); - if (p1!=~0U) { - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff), - pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); - } else { - need_input_copy = true; - CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff), - pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); - } - _cimg_mp_return(pos); - } - - if ((*ss=='i' || *ss=='j') && *ss1=='[' && - (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a scalar - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss3,s0++,depth1,0,is_single); - } else { p1 = ~0U; s0 = ss2; } - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(s0,s1,depth1,0,is_single); // Offset - arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):~0U; // Boundary - if (p_ref && arg2==~0U) { - *p_ref = 2; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization - if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2; - } - if (p1!=~0U) { - if (!listin) _cimg_mp_return(0); - pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2); - } else { - if (!imgin) _cimg_mp_return(0); - need_input_copy = true; - pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2); - } - memtype[pos] = -2; // Prevent from being used in further optimization - _cimg_mp_return(pos); - } - - s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; - if (s0>ss) { // Vector value - arg1 = compile(ss,s0,depth1,0,is_single); - if (_cimg_mp_is_scalar(arg1)) { - variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - - } - s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - - if (s1<se1) { // Two arguments -> sub-vector extraction - p1 = _cimg_mp_size(arg1); - arg2 = compile(++s0,s1,depth1,0,is_single); // Starting indice - arg3 = compile(++s1,se1,depth1,0,is_single); // Length - _cimg_mp_check_constant(arg3,2,3); - arg3 = (unsigned int)mem[arg3]; - pos = vector(arg3); - CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3).move_to(code); - _cimg_mp_return(pos); - } - - // One argument -> vector value reference - arg2 = compile(++s0,se1,depth1,0,is_single); - if (_cimg_mp_is_constant(arg2)) { // Constant index - nb = (int)mem[arg2]; - if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); - variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " - "(vector '%s' has dimension %u), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - variable_name._data,nb, - variable_name._data,_cimg_mp_size(arg1), - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - if (p_ref) { - *p_ref = 1; - p_ref[1] = arg1; - p_ref[2] = arg2; - if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; // Prevent from being used in further optimization - } - pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2); - memtype[pos] = -2; // Prevent from being used in further optimization - _cimg_mp_return(pos); - } - } - - // Look for a function call, an access to image value, or a parenthesis. - if (*se1==')') { - if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_single)); // Simple parentheses - _cimg_mp_op("Value accessor '()'"); - is_relative = *ss=='j' || *ss=='J'; - s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } - - // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions) - if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss3,s0++,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { p1 = ~0U; s0 = ss2; } - arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x; - arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y; - arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z; - arg4 = arg5 = ~0U; - if (s0<se1) { - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(s0,s1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector - p2 = _cimg_mp_size(arg1); - ++arg1; - if (p2>1) { - arg2 = arg1 + 1; - if (p2>2) arg3 = arg2 + 1; - } - if (s1<se1) { - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg4 = compile(s1,s2,depth1,0,is_single); - arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U; - } - } else if (s1<se1) { - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(s1,s2,depth1,0,is_single); - if (s2<se1) { - s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg3 = compile(s2,s3,depth1,0,is_single); - if (s3<se1) { - s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg4 = compile(s3,s2,depth1,0,is_single); - arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U; - } - } - } - } - if (p_ref && arg4==~0U && arg5==~0U) { - *p_ref = 5; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - p_ref[4] = arg2; - p_ref[5] = arg3; - if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization - if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2; - if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; - if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; - } - p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any - if (p1==~0U) p2 = imgin._spectrum; - else if (_cimg_mp_is_constant(p1)) { - p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - p2 = listin[p3]._spectrum; - } - if (!p2) _cimg_mp_return(0); - pos = vector(p2); - if (p1!=~0U) - CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz), - pos,p1,arg1,arg2,arg3, - arg4==~0U?_cimg_mp_interpolation:arg4, - arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); - else { - need_input_copy = true; - CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz), - pos,arg1,arg2,arg3, - arg4==~0U?_cimg_mp_interpolation:arg4, - arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); - } - _cimg_mp_return(pos); - } - - // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions) - if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar - if (*ss2=='#') { // Index specified - s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss3,s0++,depth1,0,is_single); - } else { p1 = ~0U; s0 = ss2; } - arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x; - arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y; - arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z; - arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c; - arg5 = arg6 = ~0U; - if (s0<se1) { - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(s0,s1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector - p2 = _cimg_mp_size(arg1); - ++arg1; - if (p2>1) { - arg2 = arg1 + 1; - if (p2>2) { - arg3 = arg2 + 1; - if (p2>3) arg4 = arg3 + 1; - } - } - if (s1<se1) { - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg5 = compile(s1,s2,depth1,0,is_single); - arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U; - } - } else if (s1<se1) { - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(s1,s2,depth1,0,is_single); - if (s2<se1) { - s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg3 = compile(s2,s3,depth1,0,is_single); - if (s3<se1) { - s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg4 = compile(s3,s2,depth1,0,is_single); - if (s2<se1) { - s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg5 = compile(s2,s3,depth1,0,is_single); - arg6 = s3<se1?compile(++s3,se1,depth1,0,is_single):~0U; - } - } - } - } - } - if (p_ref && arg5==~0U && arg6==~0U) { - *p_ref = 3; - p_ref[1] = p1; - p_ref[2] = (unsigned int)is_relative; - p_ref[3] = arg1; - p_ref[4] = arg2; - p_ref[5] = arg3; - p_ref[6] = arg4; - if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization - if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2; - if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; - if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; - if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2; - } - - if (p1!=~0U) { - if (!listin) _cimg_mp_return(0); - pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc, - p1,arg1,arg2,arg3,arg4, - arg5==~0U?_cimg_mp_interpolation:arg5, - arg6==~0U?_cimg_mp_boundary:arg6); - } else { - if (!imgin) _cimg_mp_return(0); - need_input_copy = true; - pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc, - arg1,arg2,arg3,arg4, - arg5==~0U?_cimg_mp_interpolation:arg5, - arg6==~0U?_cimg_mp_boundary:arg6); - } - memtype[pos] = -2; // Prevent from being used in further optimization - _cimg_mp_return(pos); - } - - // Mathematical functions. - switch (*ss) { - - case '_' : - if (*ss1=='(') // Skip arguments - _cimg_mp_return_nan(); - break; - - case 'a' : - if (!std::strncmp(ss,"abs(",4)) { // Absolute value - _cimg_mp_op("Function 'abs()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::abs(mem[arg1])); - _cimg_mp_scalar1(mp_abs,arg1); - } - - if (!std::strncmp(ss,"acos(",5)) { // Arccos - _cimg_mp_op("Function 'acos()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::acos(mem[arg1])); - _cimg_mp_scalar1(mp_acos,arg1); - } - - if (!std::strncmp(ss,"acosh(",6)) { // Hyperbolic arccosine - _cimg_mp_op("Function 'acosh()'"); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acosh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::acosh(mem[arg1])); - _cimg_mp_scalar1(mp_acosh,arg1); - } - - if (!std::strncmp(ss,"asinh(",6)) { // Hyperbolic arcsine - _cimg_mp_op("Function 'asinh()'"); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asinh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::asinh(mem[arg1])); - _cimg_mp_scalar1(mp_asinh,arg1); - } - - if (!std::strncmp(ss,"atanh(",6)) { // Hyperbolic arctangent - _cimg_mp_op("Function 'atanh()'"); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atanh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::atanh(mem[arg1])); - _cimg_mp_scalar1(mp_atanh,arg1); - } - - if (!std::strncmp(ss,"arg(",4)) { // Nth argument - _cimg_mp_op("Function 'arg()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(s1,s2,depth1,0,is_single); - p2 = _cimg_mp_size(arg2); - p3 = 3; - CImg<ulongT>::vector((ulongT)mp_arg,0,0,p2,arg1,arg2).move_to(l_opcode); - for (s = ++s2; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg3 = compile(s,ns,depth1,0,is_single); - _cimg_mp_check_type(arg3,p3,p2?2:1,p2); - CImg<ulongT>::vector(arg3).move_to(l_opcode); - ++p3; - s = ns; - } - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - if (_cimg_mp_is_constant(arg1)) { - p3-=1; // Number of args - arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]); - if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]); - if (p2) { - pos = vector(p2); - std::memset(&mem[pos] + 1,0,p2*sizeof(double)); - _cimg_mp_return(pos); - } else _cimg_mp_return(0); - } - pos = opcode[1] = p2?vector(p2):scalar(); - opcode.move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"asin(",5)) { // Arcsin - _cimg_mp_op("Function 'asin()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1])); - _cimg_mp_scalar1(mp_asin,arg1); - } - - if (!std::strncmp(ss,"atan(",5)) { // Arctan - _cimg_mp_op("Function 'atan()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1])); - _cimg_mp_scalar1(mp_atan,arg1); - } - - if (!std::strncmp(ss,"atan2(",6)) { // Arctan2 - _cimg_mp_op("Function 'atan2()'"); - s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss6,s1,depth1,0,is_single); - arg2 = compile(++s1,se1,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2])); - _cimg_mp_scalar2(mp_atan2,arg1,arg2); - } - break; - - case 'b' : - if (!std::strncmp(ss,"break(",6)) { // Complex absolute value - if (pexpr[se2 - expr._data]=='(') { // no arguments? - CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return_nan(); - } - } - - if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test) - _cimg_mp_op("Function 'breakpoint()'"); - if (pexpr[se2 - expr._data]=='(') { // no arguments? - CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return_nan(); - } - } - - if (!std::strncmp(ss,"bool(",5)) { // Boolean cast - _cimg_mp_op("Function 'bool()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); - _cimg_mp_scalar1(mp_bool,arg1); - } - - if (!std::strncmp(ss,"begin(",6)) { // Begin - _cimg_mp_op("Function 'begin()'"); - code.swap(code_begin); - arg1 = compile(ss6,se1,depth1,p_ref,true); - code.swap(code_begin); - _cimg_mp_return(arg1); - } - break; - - case 'c' : - if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value - _cimg_mp_op("Function 'cabs()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,0,2,2); - _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2); - } - - if (!std::strncmp(ss,"carg(",5)) { // Complex argument - _cimg_mp_op("Function 'carg()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,0,2,2); - _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1); - } - - if (!std::strncmp(ss,"cats(",5)) { // Concatenate strings - _cimg_mp_op("Function 'cats()'"); - CImg<ulongT>::vector((ulongT)mp_cats,0,0,0).move_to(l_opcode); - arg1 = 0; - for (s = ss5; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg1 = compile(s,ns,depth1,0,is_single); - CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); - s = ns; - } - _cimg_mp_check_constant(arg1,1,3); // Last argument = output vector size - l_opcode.remove(); - (l_opcode>'y').move_to(opcode); - p1 = (unsigned int)mem[arg1]; - pos = vector(p1); - opcode[1] = pos; - opcode[2] = p1; - opcode[3] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root - _cimg_mp_op("Function 'cbrt()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1])); - _cimg_mp_scalar1(mp_cbrt,arg1); - } - - if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate - _cimg_mp_op("Function 'cconj()'"); - arg1 = compile(ss6,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,0,2,2); - pos = vector(2); - CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"ceil(",5)) { // Ceil - _cimg_mp_op("Function 'ceil()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1])); - _cimg_mp_scalar1(mp_ceil,arg1); - } - - if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential - _cimg_mp_op("Function 'cexp()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,0,2,2); - pos = vector(2); - CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm - _cimg_mp_op("Function 'clog()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,0,2,2); - pos = vector(2); - CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"continue(",9)) { // Complex absolute value - if (pexpr[se2 - expr._data]=='(') { // no arguments? - CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return_nan(); - } - } - - if (!std::strncmp(ss,"copy(",5)) { // Memory copy - _cimg_mp_op("Function 'copy()'"); - ref.assign(14); - s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = p1 = compile(ss5,s1,depth1,ref,is_single); - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(s1,s2,depth1,ref._data + 7,is_single); - arg3 = arg4 = arg5 = ~0U; arg6 = 1; - if (s2<se1) { - s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg3 = compile(s2,s3,depth1,0,is_single); - if (s3<se1) { - s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg4 = compile(s3,s1,depth1,0,is_single); - if (s1<se1) { - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg5 = compile(s1,s2,depth1,0,is_single); - arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):1; - } - } - } - if (_cimg_mp_is_vector(arg1)) { - if (!ref[0]) ++arg1; - else if (ref[0]>=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]); - } - if (_cimg_mp_is_vector(arg2)) { - if (arg3==~0U) arg3 = constant(_cimg_mp_size(arg2)); - if (!ref[7]) ++arg2; - if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]); - } - if (arg3==~0U) arg3 = 1; - if (arg4==~0U) arg4 = 1; - if (arg5==~0U) arg5 = 1; - _cimg_mp_check_type(arg3,3,1,0); - _cimg_mp_check_type(arg4,4,1,0); - _cimg_mp_check_type(arg5,5,1,0); - _cimg_mp_check_type(arg6,5,1,0); - CImg<ulongT>(1,22).move_to(code); - code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6); - code.back().get_shared_rows(8,21).fill(ref); - _cimg_mp_return(p1); - } - - if (!std::strncmp(ss,"cos(",4)) { // Cosine - _cimg_mp_op("Function 'cos()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1])); - _cimg_mp_scalar1(mp_cos,arg1); - } - - if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine - _cimg_mp_op("Function 'cosh()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1])); - _cimg_mp_scalar1(mp_cosh,arg1); - } - - if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time) - _cimg_mp_op("Function 'critical()'"); - p1 = code._width; - arg1 = compile(ss + 9,se1,depth1,p_ref,true); - CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1); - _cimg_mp_return(arg1); - } - - if (!std::strncmp(ss,"crop(",5)) { // Image crop - _cimg_mp_op("Function 'crop()'"); - if (*ss5=='#') { // Index specified - s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss6,s0++,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { p1 = ~0U; s0 = ss5; need_input_copy = true; } - pos = 0; - is_sth = false; // Coordinates specified as a vector? - if (s0<se1) for (s = s0; s<se; ++s, ++pos) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg1 = compile(s,ns,depth1,0,is_single); - if (!pos && _cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector - opcode = CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1, - arg1 + (ulongT)_cimg_mp_size(arg1)); - opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode); - is_sth = true; - } else { - _cimg_mp_check_type(arg1,pos + 1,1,0); - CImg<ulongT>::vector(arg1).move_to(l_opcode); - } - s = ns; - } - (l_opcode>'y').move_to(opcode); - - arg1 = 0; arg2 = (p1!=~0U); - switch (opcode._height) { - case 0 : case 1 : - CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); - break; - case 2 : - CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); - arg1 = arg2 + 2; - break; - case 3 : - CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); - arg1 = arg2 + 2; - break; - case 4 : - CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary). - move_to(opcode); - arg1 = arg2 + (is_sth?2:3); - break; - case 5 : - CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). - move_to(opcode); - arg1 = arg2 + (is_sth?2:3); - break; - case 6 : - CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, - _cimg_mp_boundary).move_to(opcode); - arg1 = arg2 + (is_sth?2:4); - break; - case 7 : - CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, - opcode[6]).move_to(opcode); - arg1 = arg2 + (is_sth?2:4); - break; - case 8 : - CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], - opcode[7],_cimg_mp_boundary).move_to(opcode); - arg1 = arg2 + (is_sth?2:5); - break; - case 9 : - arg1 = arg2 + (is_sth?2:5); - break; - default : // Error -> too much arguments - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Too much arguments specified, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - - _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0); - _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0); - _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0); - _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0); - if (opcode[4]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3); - opcode[4] = (ulongT)mem[opcode[4]]; - } - if (opcode[5]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3); - opcode[5] = (ulongT)mem[opcode[5]]; - } - if (opcode[6]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3); - opcode[6] = (ulongT)mem[opcode[6]]; - } - if (opcode[7]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3); - opcode[7] = (ulongT)mem[opcode[7]]; - } - _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0); - - if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U || - opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) { - if (p1!=~0U) { - _cimg_mp_check_constant(p1,1,1); - p1 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - } - const CImg<T> &img = p1!=~0U?listin[p1]:imgin; - if (!img) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Cannot crop empty image when " - "some xyzc-coordinates are unspecified, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width; - if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height; - if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth; - if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum; - } - - pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7])); - CImg<ulongT>::vector((ulongT)mp_crop, - pos,p1, - *opcode,opcode[1],opcode[2],opcode[3], - opcode[4],opcode[5],opcode[6],opcode[7], - opcode[8]).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"cross(",6)) { // Cross product - _cimg_mp_op("Function 'cross()'"); - s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss6,s1,depth1,0,is_single); - arg2 = compile(++s1,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,2,3); - _cimg_mp_check_type(arg2,2,2,3); - pos = vector(3); - CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"cut(",4)) { // Cut - _cimg_mp_op("Function 'cut()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = compile(++s2,se1,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) { - val = mem[arg1]; - val1 = mem[arg2]; - val2 = mem[arg3]; - _cimg_mp_constant(val<val1?val1:val>val2?val2:val); - } - _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); - } - break; - - case 'd' : - if (*ss1=='(') { // Image depth - _cimg_mp_op("Function 'd()'"); - if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_d,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"date(",5)) { // Current date or file date - _cimg_mp_op("Function 'date()'"); - s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = ss5!=se1?compile(ss5,s1,depth1,0,is_single):~0U; - is_sth = s1++!=se1; // is_filename - pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar(); - if (is_sth) { - *se1 = 0; - variable_name.assign(CImg<charT>::string(s1,true,true).unroll('y'),true); - cimg::strpare(variable_name,false,true); - ((CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)),variable_name)>'y'). - move_to(opcode); - *se1 = ')'; - } else - CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)).move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"debug(",6)) { // Print debug info - _cimg_mp_op("Function 'debug()'"); - p1 = code._width; - arg1 = compile(ss6,se1,depth1,p_ref,is_single); - *se1 = 0; - variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true); - cimg::strpare(variable_name,false,true); - ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1), - variable_name)>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code,p1); - *se1 = ')'; - _cimg_mp_return(arg1); - } - - if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image - _cimg_mp_op("Function 'display()'"); - if (pexpr[se2 - expr._data]=='(') { // no arguments? - CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return_nan(); - } - if (*ss8!='#') { // Vector - s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss8,s1,depth1,0,is_single); - arg2 = 0; arg3 = arg4 = arg5 = 1; - if (s1<se1) { - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(s1 + 1,s2,depth1,0,is_single); - if (s2<se1) { - s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg3 = compile(s2,s3,depth1,0,is_single); - if (s3<se1) { - s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg4 = compile(s3,s2,depth1,0,is_single); - arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):0; - } - } - } - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - _cimg_mp_check_type(arg4,4,1,0); - _cimg_mp_check_type(arg5,5,1,0); - - c1 = *s1; *s1 = 0; - variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true); - cimg::strpare(variable_name,false,true); - if (_cimg_mp_is_vector(arg1)) - ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0), - variable_name)>'y').move_to(opcode); - else - ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0), - variable_name)>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - - ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1), - arg2,arg3,arg4,arg5), - variable_name)>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - *s1 = c1; - _cimg_mp_return(arg1); - - } else { // Image - p1 = compile(ss8 + 1,se1,depth1,0,is_single); - _cimg_mp_check_list(true); - CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code); - _cimg_mp_return_nan(); - } - } - - if (!std::strncmp(ss,"det(",4)) { // Matrix determinant - _cimg_mp_op("Function 'det()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); - _cimg_mp_scalar2(mp_det,arg1,p1); - } - - if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix - _cimg_mp_op("Function 'diag()'"); - CImg<ulongT>::vector((ulongT)mp_diag,0,0).move_to(l_opcode); - for (s = ss5; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg2 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg2)) - CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_size(arg2)). - move_to(l_opcode); - else CImg<ulongT>::vector(arg2).move_to(l_opcode); - s = ns; - } - (l_opcode>'y').move_to(opcode); - arg1 = opcode._height - 3; - pos = vector(arg1*arg1); - opcode[1] = pos; - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"dot(",4)) { // Dot product - _cimg_mp_op("Function 'dot()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - arg2 = compile(++s1,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) { - _cimg_mp_check_type(arg2,2,2,_cimg_mp_size(arg1)); - _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1)); - } - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_scalar2(mp_mul,arg1,arg2); - } - - if (!std::strncmp(ss,"do(",3)) { // Do..while - _cimg_mp_op("Function 'do()'"); - s0 = *ss2=='('?ss3:ss8; - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = code._width; - arg6 = mempos; - p1 = compile(s0,s1,depth1,0,is_single); // Body - arg2 = code._width; - p2 = s1<se1?compile(++s1,se1,depth1,0,is_single):p1; // Condition - _cimg_mp_check_type(p2,2,1,0); - CImg<ulongT>::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1), - p1>=arg6 && !_cimg_mp_is_constant(p1), - p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); - _cimg_mp_return(p1); - } - - if (!std::strncmp(ss,"draw(",5)) { // Draw image - if (!is_single) is_parallelizable = false; - _cimg_mp_op("Function 'draw()'"); - if (*ss5=='#') { // Index specified - s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss6,s0++,depth1,0,is_single); - _cimg_mp_check_list(true); - } else { p1 = ~0U; s0 = ss5; } - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(s0,s1,depth1,0,is_single); - arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_x; - arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_y; - arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_z; - arg5 = is_relative?0U:(unsigned int)_cimg_mp_slot_c; - s0 = se1; - if (s1<se1) { - s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - arg2 = compile(++s1,s0,depth1,0,is_single); - if (_cimg_mp_is_vector(arg2)) { // Coordinates specified as a vector - p2 = _cimg_mp_size(arg2); - ++arg2; - if (p2>1) { - arg3 = arg2 + 1; - if (p2>2) { - arg4 = arg3 + 1; - if (p2>3) arg5 = arg4 + 1; - } - } - ++s0; - is_sth = true; - } else { - if (s0<se1) { - is_sth = p1!=~0U; - s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg3 = compile(++s0,s1,depth1,0,is_single); - _cimg_mp_check_type(arg3,is_sth?4:3,1,0); - if (s1<se1) { - s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - arg4 = compile(++s1,s0,depth1,0,is_single); - _cimg_mp_check_type(arg4,is_sth?5:4,1,0); - if (s0<se1) { - s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg5 = compile(++s0,s1,depth1,0,is_single); - _cimg_mp_check_type(arg5,is_sth?6:5,1,0); - s0 = ++s1; - } - } - } - is_sth = false; - } - } - - l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'! - CImg<ulongT>::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5, - 0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode); - - arg2 = arg3 = arg4 = arg5 = ~0U; - p2 = p1!=~0U?0:1; - if (s0<se1) { - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg2 = compile(s0,s1,depth1,0,is_single); - _cimg_mp_check_type(arg2,p2 + (is_sth?3:6),1,0); - if (s1<se1) { - s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - arg3 = compile(++s1,s0,depth1,0,is_single); - _cimg_mp_check_type(arg3,p2 + (is_sth?4:7),1,0); - if (s0<se1) { - s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg4 = compile(++s0,s1,depth1,0,is_single); - _cimg_mp_check_type(arg4,p2 + (is_sth?5:8),1,0); - if (s1<se1) { - s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - arg5 = compile(++s1,s0,depth1,0,is_single); - _cimg_mp_check_type(arg5,p2 + (is_sth?6:9),1,0); - } - } - } - } - if (s0<s1) s0 = s1; - - l_opcode(0,8) = (ulongT)arg2; - l_opcode(0,9) = (ulongT)arg3; - l_opcode(0,10) = (ulongT)arg4; - l_opcode(0,11) = (ulongT)arg5; - - if (s0<se1) { - s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg6 = compile(++s0,s1,depth1,0,is_single); - _cimg_mp_check_type(arg6,0,1,0); - l_opcode(0,12) = arg6; - if (s1<se1) { - s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p2 = compile(++s1,s0,depth1,0,is_single); - _cimg_mp_check_type(p2,0,2,0); - l_opcode(0,13) = p2; - l_opcode(0,14) = _cimg_mp_size(p2); - p3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1; - _cimg_mp_check_type(p3,0,1,0); - l_opcode(0,15) = p3; - } - } - l_opcode[0].move_to(code); - _cimg_mp_return(arg1); - } - - break; - - case 'e' : - if (!std::strncmp(ss,"echo(",5)) { // Echo - _cimg_mp_op("Function 'echo()'"); - CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode); - for (s = ss5; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg1 = compile(s,ns,depth1,0,is_single); - CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); - s = ns; - } - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return_nan(); - } - - if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector - _cimg_mp_op("Function 'eig()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); - pos = vector((p1 + 1)*p1); - CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"end(",4)) { // End - _cimg_mp_op("Function 'end()'"); - code.swap(code_end); - compile(ss4,se1,depth1,p_ref,true); - code.swap(code_end); - _cimg_mp_return_nan(); - } - - if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing - if (!is_single) is_parallelizable = false; - _cimg_mp_op("Function 'ellipse()'"); - if (*ss8=='#') { // Index specified - s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss + 9,s0++,depth1,0,is_single); - _cimg_mp_check_list(true); - } else { p1 = ~0U; s0 = ss8; } - if (s0==se1) compile(s0,se1,depth1,0,is_single); // 'missing' argument error - CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); - for (s = s0; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg2 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg2)) - CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_size(arg2)). - move_to(l_opcode); - else CImg<ulongT>::vector(arg2).move_to(l_opcode); - s = ns; - } - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return_nan(); - } - - if (!std::strncmp(ss,"ext(",4)) { // Extern - _cimg_mp_op("Function 'ext()'"); - if (!is_single) is_parallelizable = false; - CImg<ulongT>::vector((ulongT)mp_ext,0,0).move_to(l_opcode); - pos = 1; - for (s = ss4; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg1 = compile(s,ns,depth1,0,is_single); - CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); - s = ns; - } - (l_opcode>'y').move_to(opcode); - pos = scalar(); - opcode[1] = pos; - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"exp(",4)) { // Exponential - _cimg_mp_op("Function 'exp()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); - _cimg_mp_scalar1(mp_exp,arg1); - } - - if (!std::strncmp(ss,"eye(",4)) { // Identity matrix - _cimg_mp_op("Function 'eye()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - _cimg_mp_check_constant(arg1,1,3); - p1 = (unsigned int)mem[arg1]; - pos = vector(p1*p1); - CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'f' : - if (!std::strncmp(ss,"fact(",5)) { // Factorial - _cimg_mp_op("Function 'fact()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1])); - _cimg_mp_scalar1(mp_factorial,arg1); - } - - if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci - _cimg_mp_op("Function 'fibo()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1])); - _cimg_mp_scalar1(mp_fibonacci,arg1); - } - - if (!std::strncmp(ss,"find(",5)) { // Find - _cimg_mp_op("Function 'find()'"); - - // First argument: data to look at. - s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - if (*ss5=='#') { // Index specified - p1 = compile(ss6,s0,depth1,0,is_single); - _cimg_mp_check_list(false); - arg1 = ~0U; - } else { // Vector specified - arg1 = compile(ss5,s0,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,2,0); - p1 = ~0U; - } - - // Second argument: data to find. - s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg2 = compile(s0,s1,depth1,0,is_single); - - // Third and fourth arguments: search direction and starting index. - arg3 = 1; arg4 = _cimg_mp_slot_nan; - if (s1<se1) { - s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - arg3 = compile(++s1,s0,depth1,0,is_single); - _cimg_mp_check_type(arg3,3,1,0); - if (s0<se1) { - arg4 = compile(++s0,se1,depth1,0,is_single); - _cimg_mp_check_type(arg4,4,1,0); - } - } - if (p1!=~0U) { - if (_cimg_mp_is_vector(arg2)) - _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4); - _cimg_mp_scalar4(mp_list_find,p1,arg2,arg3,arg4); - } - if (_cimg_mp_is_vector(arg2)) - _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4); - _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2,arg3,arg4); - } - - if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop - _cimg_mp_op("Function 'for()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg1 = code._width; - p1 = compile(ss4,s1,depth1,0,is_single); // Init - arg2 = code._width; - p2 = compile(++s1,s2,depth1,0,is_single); // Cond - arg3 = code._width; - arg6 = mempos; - if (s3<se1) { // Body + post - p3 = compile(s3 + 1,se1,depth1,0,is_single); // Body - arg4 = code._width; - pos = compile(++s2,s3,depth1,0,is_single); // Post - } else { - p3 = compile(++s2,se1,depth1,0,is_single); // Body only - arg4 = pos = code._width; - } - _cimg_mp_check_type(p2,2,1,0); - arg5 = _cimg_mp_size(pos); - CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2, - arg4 - arg3,code._width - arg4, - p3>=arg6 && !_cimg_mp_is_constant(p3), - p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); - _cimg_mp_return(p3); - } - - if (!std::strncmp(ss,"floor(",6)) { // Floor - _cimg_mp_op("Function 'floor()'"); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1])); - _cimg_mp_scalar1(mp_floor,arg1); - } - - if (!std::strncmp(ss,"fsize(",6)) { // File size - _cimg_mp_op("Function 'fsize()'"); - *se1 = 0; - variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true); - cimg::strpare(variable_name,false,true); - pos = scalar(); - ((CImg<ulongT>::vector((ulongT)mp_fsize,pos,0),variable_name)>'y').move_to(opcode); - *se1 = ')'; - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'g' : - if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function - _cimg_mp_op("Function 'gauss()'"); - s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss6,s1,depth1,0,is_single); - arg2 = arg3 = 1; - if (s1<se1) { - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1; - } - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) { - val1 = mem[arg1]; - val2 = mem[arg2]; - _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1)); - } - _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3); - } - - if (!std::strncmp(ss,"gcd(",4)) { // Gcd - _cimg_mp_op("Function 'gcd()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - arg2 = compile(++s1,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - _cimg_mp_check_type(arg2,2,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(cimg::gcd((long)mem[arg1],(long)mem[arg2])); - _cimg_mp_scalar2(mp_gcd,arg1,arg2); - } - break; - - case 'h' : - if (*ss1=='(') { // Image height - _cimg_mp_op("Function 'h()'"); - if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_h,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'i' : - if (*ss1=='c' && *ss2=='(') { // Image median - _cimg_mp_op("Function 'ic()'"); - if (*ss3=='#') { // Index specified - p1 = compile(ss4,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss3!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (*ss1=='f' && *ss2=='(') { // If..then[..else.] - _cimg_mp_op("Function 'if()'"); - s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg1 = compile(ss3,s1,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - if (_cimg_mp_is_constant(arg1)) { - if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,is_single); - else return s2<se1?compile(++s2,se1,depth1,0,is_single):0; - } - p2 = code._width; - arg2 = compile(++s1,s2,depth1,0,is_single); - p3 = code._width; - arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single): - _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0; - _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2)); - arg4 = _cimg_mp_size(arg2); - if (arg4) pos = vector(arg4); else pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3, - p3 - p2,code._width - p3,arg4).move_to(code,p2); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"int(",4)) { // Integer cast - _cimg_mp_op("Function 'int()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]); - _cimg_mp_scalar1(mp_int,arg1); - } - - if (!std::strncmp(ss,"inv(",4)) { // Matrix/scalar inversion - _cimg_mp_op("Function 'inv()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) { - _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); - pos = vector(p1*p1); - CImg<ulongT>::vector((ulongT)mp_matrix_inv,pos,arg1,p1).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]); - _cimg_mp_scalar2(mp_div,1,arg1); - } - - if (*ss1=='s') { // Family of 'is_?()' functions - - if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? - _cimg_mp_op("Function 'isbool()'"); - if (ss7==se1) _cimg_mp_return(0); - arg1 = compile(ss7,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.); - _cimg_mp_scalar1(mp_isbool,arg1); - } - - if (!std::strncmp(ss,"isdir(",6)) { // Is directory? - _cimg_mp_op("Function 'isdir()'"); - *se1 = 0; - is_sth = cimg::is_directory(ss6); - *se1 = ')'; - _cimg_mp_return(is_sth?1U:0U); - } - - if (!std::strncmp(ss,"isfile(",7)) { // Is file? - _cimg_mp_op("Function 'isfile()'"); - *se1 = 0; - is_sth = cimg::is_file(ss7); - *se1 = ')'; - _cimg_mp_return(is_sth?1U:0U); - } - - if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector? - if (ss5>=se1) _cimg_mp_return(0); - _cimg_mp_op("Function 'isin()'"); - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode); - for (s = ss5; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg1 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) - CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1, - arg1 + (ulongT)_cimg_mp_size(arg1)). - move_to(l_opcode); - else CImg<ulongT>::vector(arg1).move_to(l_opcode); - s = ns; - } - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? - _cimg_mp_op("Function 'isinf()'"); - if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1])); - _cimg_mp_scalar1(mp_isinf,arg1); - } - - if (!std::strncmp(ss,"isint(",6)) { // Is integer? - _cimg_mp_op("Function 'isint()'"); - if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)(cimg::mod(mem[arg1],1.)==0)); - _cimg_mp_scalar1(mp_isint,arg1); - } - - if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? - _cimg_mp_op("Function 'isnan()'"); - if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1])); - _cimg_mp_scalar1(mp_isnan,arg1); - } - - if (!std::strncmp(ss,"isval(",6)) { // Is value? - _cimg_mp_op("Function 'isval()'"); - val = 0; - if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); - _cimg_mp_return(0); - } - - } - break; - - case 'l' : - if (*ss1=='(') { // Size of image list - _cimg_mp_op("Function 'l()'"); - if (ss2!=se1) break; - _cimg_mp_scalar0(mp_list_l); - } - - if (!std::strncmp(ss,"log(",4)) { // Natural logarithm - _cimg_mp_op("Function 'log()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); - _cimg_mp_scalar1(mp_log,arg1); - } - - if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm - _cimg_mp_op("Function 'log2()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); - _cimg_mp_scalar1(mp_log2,arg1); - } - - if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm - _cimg_mp_op("Function 'log10()'"); - arg1 = compile(ss6,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); - _cimg_mp_scalar1(mp_log10,arg1); - } - - if (!std::strncmp(ss,"lowercase(",10)) { // Lower case - _cimg_mp_op("Function 'lowercase()'"); - arg1 = compile(ss + 10,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1])); - _cimg_mp_scalar1(mp_lowercase,arg1); - } - break; - - case 'm' : - if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication - _cimg_mp_op("Function 'mul()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1; - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_type(arg2,2,2,0); - _cimg_mp_check_constant(arg3,3,3); - p1 = _cimg_mp_size(arg1); - p2 = _cimg_mp_size(arg2); - p3 = (unsigned int)mem[arg3]; - arg5 = p2/p3; - arg4 = p1/arg5; - if (arg4*arg5!=p1 || arg5*p3!=p2) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') " - "do not match with third argument 'nb_colsB=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data,p3, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(arg4*p3); - CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'n' : - if (!std::strncmp(ss,"narg(",5)) { // Number of arguments - _cimg_mp_op("Function 'narg()'"); - if (ss5>=se1) _cimg_mp_return(0); - arg1 = 0; - for (s = ss5; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - ++arg1; s = ns; - } - _cimg_mp_constant(arg1); - } - - if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') || - !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) || - (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm - _cimg_mp_op("Function 'normP()'"); - if (*ss4=='(') { arg1 = 2; s = ss5; } - else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; } - else if (arg1==~0U) { - arg1 = compile(ss4,s++,depth1,0,is_single); - _cimg_mp_check_constant(arg1,0,2); - arg1 = (unsigned int)mem[arg1]; - } else s = std::strchr(ss4,'(') + 1; - pos = scalar(); - switch (arg1) { - case 0 : - CImg<ulongT>::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break; - case 1 : - CImg<ulongT>::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break; - case 2 : - CImg<ulongT>::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break; - case ~0U : - CImg<ulongT>::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break; - default : - CImg<ulongT>::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). - move_to(l_opcode); - } - for ( ; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg2 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg2)) - CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_size(arg2)). - move_to(l_opcode); - else CImg<ulongT>::vector(arg2).move_to(l_opcode); - s = ns; - } - - (l_opcode>'y').move_to(opcode); - if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 - _cimg_mp_scalar1(mp_abs,opcode[3]); - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'p' : - if (!std::strncmp(ss,"permut(",7)) { // Number of permutations - _cimg_mp_op("Function 'permut()'"); - s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg1 = compile(ss7,s1,depth1,0,is_single); - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = compile(++s2,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) - _cimg_mp_constant(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3])); - _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3); - } - - if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing - if (!is_single) is_parallelizable = false; - _cimg_mp_op("Function 'polygon()'"); - if (*ss8=='#') { // Index specified - s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss + 9,s0++,depth1,0,is_single); - _cimg_mp_check_list(true); - } else { p1 = ~0U; s0 = ss8; } - if (s0==se1) compile(s0,se1,depth1,0,is_single); // 'missing' argument error - CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); - for (s = s0; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg2 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg2)) - CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_size(arg2)). - move_to(l_opcode); - else CImg<ulongT>::vector(arg2).move_to(l_opcode); - s = ns; - } - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return_nan(); - } - - if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions - is_sth = ss[5]=='s'; // is prints() - _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'"); - s0 = is_sth?ss7:ss6; - if (*s0!='#' || is_sth) { // Regular expression - for (s = s0; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - pos = compile(s,ns,depth1,p_ref,is_single); - c1 = *ns; *ns = 0; - variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true); - cimg::strpare(variable_name,false,true); - if (_cimg_mp_is_vector(pos)) // Vector - ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0), - variable_name)>'y').move_to(opcode); - else // Scalar - ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0), - variable_name)>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - *ns = c1; s = ns; - } - _cimg_mp_return(pos); - } else { // Image - p1 = compile(ss7,se1,depth1,0,is_single); - _cimg_mp_check_list(true); - CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); - _cimg_mp_return_nan(); - } - } - - if (!std::strncmp(ss,"pseudoinv(",10)) { // Matrix/scalar pseudo-inversion - _cimg_mp_op("Function 'pseudoinv()'"); - s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss + 10,s1,depth1,0,is_single); - arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1; - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_constant(arg2,2,3); - p1 = _cimg_mp_size(arg1); - p2 = (unsigned int)mem[arg2]; - p3 = p1/p2; - if (p3*p2!=p1) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Type of first argument ('%s') " - "does not match with second argument 'nb_colsA=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,p2, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(p1); - CImg<ulongT>::vector((ulongT)mp_matrix_pseudoinv,pos,arg1,p2,p3).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'r' : - if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize - _cimg_mp_op("Function 'resize()'"); - if (*ss7!='#') { // Vector - s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss7,s1,depth1,0,is_single); - s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(s1,s2,depth1,0,is_single); - arg3 = 1; - arg4 = 0; - if (s2<se1) { - s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg3 = compile(s2,s1,depth1,0,is_single); - arg4 = s1<se1?compile(++s1,se1,depth1,0,is_single):0; - } - _cimg_mp_check_constant(arg2,2,3); - arg2 = (unsigned int)mem[arg2]; - _cimg_mp_check_type(arg3,3,1,0); - _cimg_mp_check_type(arg4,4,1,0); - pos = vector(arg2); - CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1), - arg3,arg4).move_to(code); - _cimg_mp_return(pos); - - } else { // Image - if (!is_single) is_parallelizable = false; - s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - p1 = compile(ss8,s0++,depth1,0,is_single); - _cimg_mp_check_list(true); - l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'! - CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0). - move_to(l_opcode); - pos = 0; - for (s = s0; s<se && pos<10; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg1 = compile(s,ns,depth1,0,is_single); - _cimg_mp_check_type(arg1,pos + 2,1,0); - l_opcode(0,pos + 3) = arg1; - s = ns; - ++pos; - } - if (pos<1 || pos>10) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - pos<1?"Missing":"Too much", - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - l_opcode[0].move_to(code); - _cimg_mp_return_nan(); - } - } - - if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse - _cimg_mp_op("Function 'reverse()'"); - arg1 = compile(ss8,se1,depth1,0,is_single); - if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1); - p1 = _cimg_mp_size(arg1); - pos = vector(p1); - CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation - _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1; - _cimg_mp_check_type(arg2,2,1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]): - cimg::ror(mem[arg1],(unsigned int)mem[arg2])); - _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2); - } - - if (!std::strncmp(ss,"rot(",4)) { // 2D/3D rotation matrix - _cimg_mp_op("Function 'rot()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - if (s1<se1) { // 3D rotation - _cimg_mp_check_type(arg1,1,3,3); - is_sth = false; // Is coordinates as vector? - if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector - is_sth = true; - p2 = _cimg_mp_size(arg1); - ++arg1; - arg2 = arg3 = 0; - if (p2>1) { - arg2 = arg1 + 1; - if (p2>2) arg3 = arg2 + 1; - } - arg4 = compile(++s1,se1,depth1,0,is_single); - } else { - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg3 = compile(++s2,s3,depth1,0,is_single); - arg4 = compile(++s3,se1,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - } - _cimg_mp_check_type(arg4,is_sth?2:4,1,0); - pos = vector(9); - CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code); - } else { // 2D rotation - _cimg_mp_check_type(arg1,1,1,0); - pos = vector(4); - CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code); - } - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"round(",6)) { // Value rounding - _cimg_mp_op("Function 'round()'"); - s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss6,s1,depth1,0,is_single); - arg2 = 1; - arg3 = 0; - if (s1<se1) { - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0; - } - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) - _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3])); - _cimg_mp_scalar3(mp_round,arg1,arg2,arg3); - } - break; - - case 's' : - if (*ss1=='(') { // Image spectrum - _cimg_mp_op("Function 's()'"); - if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_s,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values - _cimg_mp_op("Function 'same()'"); - s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss5,s1,depth1,0,is_single); - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = 11; - arg4 = 1; - if (s2<se1) { - s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3; - arg3 = compile(++s2,s3,depth1,0,is_single); - _cimg_mp_check_type(arg3,3,1,0); - arg4 = s3<se1?compile(++s3,se1,depth1,0,is_single):1; - } - p1 = _cimg_mp_size(arg1); - p2 = _cimg_mp_size(arg2); - _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4); - } - - if (!std::strncmp(ss,"shift(",6)) { // Shift vector - _cimg_mp_op("Function 'shift()'"); - s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss6,s1,depth1,0,is_single); - arg2 = 1; arg3 = 0; - if (s1<se1) { - s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - arg2 = compile(s1,s0,depth1,0,is_single); - arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):0; - } - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - p1 = _cimg_mp_size(arg1); - pos = vector(p1); - CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"sign(",5)) { // Sign - _cimg_mp_op("Function 'sign()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1])); - _cimg_mp_scalar1(mp_sign,arg1); - } - - if (!std::strncmp(ss,"sin(",4)) { // Sine - _cimg_mp_op("Function 'sin()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1])); - _cimg_mp_scalar1(mp_sin,arg1); - } - - if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal - _cimg_mp_op("Function 'sinc()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1])); - _cimg_mp_scalar1(mp_sinc,arg1); - } - - if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine - _cimg_mp_op("Function 'sinh()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1])); - _cimg_mp_scalar1(mp_sinh,arg1); - } - - if (!std::strncmp(ss,"size(",5)) { // Vector size - _cimg_mp_op("Function 'size()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1)); - } - - if (!std::strncmp(ss,"solve(",6)) { // Solve linear system - _cimg_mp_op("Function 'solve()'"); - s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss6,s1,depth1,0,is_single); - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1; - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_type(arg2,2,2,0); - _cimg_mp_check_constant(arg3,3,3); - p1 = _cimg_mp_size(arg1); - p2 = _cimg_mp_size(arg2); - p3 = (unsigned int)mem[arg3]; - arg5 = p2/p3; - arg4 = p1/arg5; - if (arg4*arg5!=p1 || arg5*p3!=p2) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') " - "do not match with third argument 'nb_colsB=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data,p3, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(arg4*p3); - CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"sort(",5)) { // Sort vector - _cimg_mp_op("Function 'sort()'"); - if (*ss5!='#') { // Vector - s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss5,s1,depth1,0,is_single); - arg2 = arg3 = 1; - if (s1<se1) { - s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0; - arg2 = compile(s1,s0,depth1,0,is_single); - arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1; - } - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_constant(arg3,3,3); - arg3 = (unsigned int)mem[arg3]; - p1 = _cimg_mp_size(arg1); - if (p1%arg3) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Invalid specified chunk size (%u) for first argument " - "('%s'), in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - arg3,s_type(arg1)._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(p1); - CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3).move_to(code); - _cimg_mp_return(pos); - - } else { // Image - s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - p1 = compile(ss6,s1,depth1,0,is_single); - arg1 = 1; - arg2 = constant(-1.); - if (s1<se1) { - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg1 = compile(++s1,s2,depth1,0,is_single); - if (s2<se1) arg2 = compile(++s2,se1,depth1,0,is_single); - } - _cimg_mp_check_type(arg1,2,1,0); - _cimg_mp_check_type(arg2,3,1,0); - _cimg_mp_check_list(true); - CImg<ulongT>::vector((ulongT)mp_image_sort,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code); - _cimg_mp_return_nan(); - } - } - - if (!std::strncmp(ss,"sqr(",4)) { // Square - _cimg_mp_op("Function 'sqr()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); - _cimg_mp_scalar1(mp_sqr,arg1); - } - - if (!std::strncmp(ss,"sqrt(",5)) { // Square root - _cimg_mp_op("Function 'sqrt()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); - _cimg_mp_scalar1(mp_sqrt,arg1); - } - - if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed - _cimg_mp_op("Function 'srand()'"); - arg1 = ss6<se1?compile(ss6,se1,depth1,0,is_single):~0U; - if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); } - _cimg_mp_scalar0(mp_srand0); - } - - if (!std::strncmp(ss,"stats(",6)) { // Image statistics - _cimg_mp_op("Function 'stats()'"); - if (*ss6=='#') { // Index specified - p1 = compile(ss7,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss6!=se1) break; p1 = ~0U; } - pos = vector(14); - CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"stov(",5)) { // String to double - _cimg_mp_op("Function 'stov()'"); - s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss5,s1,depth1,0,is_single); - arg2 = arg3 = 0; - if (s1<se1) { - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0; - } - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_type(arg3,3,1,0); - p1 = _cimg_mp_size(arg1); - _cimg_mp_scalar4(mp_stov,arg1,p1,arg2,arg3); - } - - if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD - _cimg_mp_op("Function 'svd()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1; - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_constant(arg2,2,3); - p1 = _cimg_mp_size(arg1); - p2 = (unsigned int)mem[arg2]; - p3 = p1/p2; - if (p3*p2!=p1) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Type of first argument ('%s') " - "does not match with second argument 'nb_colsA=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,p2, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(p1 + p2 + p2*p2); - CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 't' : - if (!std::strncmp(ss,"tan(",4)) { // Tangent - _cimg_mp_op("Function 'tan()'"); - arg1 = compile(ss4,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); - _cimg_mp_scalar1(mp_tan,arg1); - } - - if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent - _cimg_mp_op("Function 'tanh()'"); - arg1 = compile(ss5,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); - _cimg_mp_scalar1(mp_tanh,arg1); - } - - if (!std::strncmp(ss,"trace(",6)) { // Matrix trace - _cimg_mp_op("Function 'trace()'"); - arg1 = compile(ss6,se1,depth1,0,is_single); - _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); - _cimg_mp_scalar2(mp_trace,arg1,p1); - } - - if (!std::strncmp(ss,"transp(",7)) { // Matrix transpose - _cimg_mp_op("Function 'transp()'"); - s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss7,s1,depth1,0,is_single); - arg2 = compile(++s1,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_constant(arg2,2,3); - p1 = _cimg_mp_size(arg1); - p2 = (unsigned int)mem[arg2]; - p3 = p1/p2; - if (p2*p3!=p1) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Size of first argument ('%s') does not match " - "second argument 'nb_cols=%u', in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,p2, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(p3*p2); - CImg<ulongT>::vector((ulongT)mp_transp,pos,arg1,p2,p3).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'u' : - if (*ss1=='(') { // Random value with uniform distribution - _cimg_mp_op("Function 'u()'"); - if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); - s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss2,s1,depth1,0,is_single); - if (s1<se1) arg2 = compile(++s1,se1,depth1,0,is_single); else { arg2 = arg1; arg1 = 0; } - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_u,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_u,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_u,arg1,arg2); - _cimg_mp_scalar2(mp_u,arg1,arg2); - } - - if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable - _cimg_mp_op("Function 'unref()'"); - arg1 = ~0U; - for (s0 = ss6; s0<se1; s0 = s1) { - if (s0>ss6 && *s0==',') ++s0; - s1 = s0; while (s1<se1 && *s1!=',') ++s1; - c1 = *s1; - if (s1>s0) { - *s1 = 0; - arg2 = arg3 = ~0U; - if (s0[0]=='w' && s0[1]=='h' && !s0[2]) arg1 = reserved_label[arg3 = 0]; - else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && !s0[3]) arg1 = reserved_label[arg3 = 1]; - else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && s0[3]=='s' && !s0[4]) - arg1 = reserved_label[arg3 = 2]; - else if (s0[0]=='p' && s0[1]=='i' && !s0[2]) arg1 = reserved_label[arg3 = 3]; - else if (s0[0]=='i' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 4]; - else if (s0[0]=='i' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 5]; - else if (s0[0]=='i' && s0[1]=='a' && !s0[2]) arg1 = reserved_label[arg3 = 6]; - else if (s0[0]=='i' && s0[1]=='v' && !s0[2]) arg1 = reserved_label[arg3 = 7]; - else if (s0[0]=='i' && s0[1]=='s' && !s0[2]) arg1 = reserved_label[arg3 = 8]; - else if (s0[0]=='i' && s0[1]=='p' && !s0[2]) arg1 = reserved_label[arg3 = 9]; - else if (s0[0]=='i' && s0[1]=='c' && !s0[2]) arg1 = reserved_label[arg3 = 10]; - else if (s0[0]=='x' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 11]; - else if (s0[0]=='y' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 12]; - else if (s0[0]=='z' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 13]; - else if (s0[0]=='c' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 14]; - else if (s0[0]=='x' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 15]; - else if (s0[0]=='y' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 16]; - else if (s0[0]=='z' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 17]; - else if (s0[0]=='c' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 18]; - else if (s0[0]=='i' && s0[1]>='0' && s0[1]<='9' && !s0[2]) - arg1 = reserved_label[arg3 = 19 + s0[1] - '0']; - else if (!std::strcmp(s0,"interpolation")) arg1 = reserved_label[arg3 = 29]; - else if (!std::strcmp(s0,"boundary")) arg1 = reserved_label[arg3 = 30]; - else if (s0[1]) { // Multi-char variable - cimglist_for(variable_def,i) if (!std::strcmp(s0,variable_def[i])) { - arg1 = variable_pos[i]; arg2 = i; break; - } - } else arg1 = reserved_label[arg3 = *s0]; // Single-char variable - - if (arg1!=~0U) { - if (arg2==~0U) { if (arg3!=~0U) reserved_label[arg3] = ~0U; } - else { - variable_def.remove(arg2); - if (arg2<variable_pos._width - 1) - std::memmove(variable_pos._data + arg2,variable_pos._data + arg2 + 1, - sizeof(uintT)*(variable_pos._width - arg2 - 1)); - --variable_pos._width; - } - } - *s1 = c1; - } else compile(s0,s1,depth1,0,is_single); // Will throw a 'missing argument' exception - } - _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable - } - - if (!std::strncmp(ss,"uppercase(",10)) { // Upper case - _cimg_mp_op("Function 'uppercase()'"); - arg1 = compile(ss + 10,se1,depth1,0,is_single); - if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::uppercase(mem[arg1])); - _cimg_mp_scalar1(mp_uppercase,arg1); - } - break; - - case 'v' : - if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) || - !std::strncmp(ss,"vector(",7) || - (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector - _cimg_mp_op("Function 'vector()'"); - arg2 = 0; // Number of specified values - if (arg1==~0U && *ss6!='(') { - arg1 = compile(ss6,s++,depth1,0,is_single); - _cimg_mp_check_constant(arg1,0,3); - arg1 = (unsigned int)mem[arg1]; - } else s = std::strchr(ss6,'(') + 1; - - if (s<se1 || arg1==~0U) for ( ; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg3 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg3)) { - arg4 = _cimg_mp_size(arg3); - CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode); - arg2+=arg4; - } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; } - s = ns; - } - if (arg1==~0U) arg1 = arg2; - if (!arg1) _cimg_mp_return(0); - pos = vector(arg1); - l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0); - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string - _cimg_mp_op("Function 'vtos()'"); - s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss5,s1,depth1,0,is_single); - arg2 = 0; arg3 = ~0U; - if (s1<se1) { - s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2; - arg2 = compile(++s1,s2,depth1,0,is_single); - arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U; - } - _cimg_mp_check_type(arg2,2,1,0); - if (arg3==~0U) { // Auto-guess best output vector size - p1 = _cimg_mp_size(arg1); - p1 = p1?22*p1 - 1:18; - } else { - _cimg_mp_check_constant(arg3,3,3); - p1 = (unsigned int)mem[arg3]; - } - pos = vector(p1); - CImg<ulongT>::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'w' : - if (*ss1=='(') { // Image width - _cimg_mp_op("Function 'w()'"); - if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_w,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (*ss1=='h' && *ss2=='(') { // Image width*height - _cimg_mp_op("Function 'wh()'"); - if (*ss3=='#') { // Index specified - p1 = compile(ss4,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss3!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_wh,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth - _cimg_mp_op("Function 'whd()'"); - if (*ss4=='#') { // Index specified - p1 = compile(ss5,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss4!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_whd,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum - _cimg_mp_op("Function 'whds()'"); - if (*ss5=='#') { // Index specified - p1 = compile(ss6,se1,depth1,0,is_single); - _cimg_mp_check_list(false); - } else { if (ss5!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg<ulongT>::vector((ulongT)mp_image_whds,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (!std::strncmp(ss,"while(",6)) { // While...do - _cimg_mp_op("Function 'while()'"); - s0 = *ss5=='('?ss6:ss8; - s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - p1 = code._width; - arg1 = compile(s0,s1,depth1,0,is_single); - p2 = code._width; - arg6 = mempos; - pos = compile(++s1,se1,depth1,0,is_single); - _cimg_mp_check_type(arg1,1,1,0); - arg2 = _cimg_mp_size(pos); - CImg<ulongT>::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2, - pos>=arg6 && !_cimg_mp_is_constant(pos), - arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1); - _cimg_mp_return(pos); - } - break; - - case 'x' : - if (!std::strncmp(ss,"xor(",4)) { // Xor - _cimg_mp_op("Function 'xor()'"); - s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1; - arg1 = compile(ss4,s1,depth1,0,is_single); - arg2 = compile(++s1,se1,depth1,0,is_single); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2); - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1] ^ (longT)mem[arg2]); - _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2); - } - break; - } - - if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) || - !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) || - !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) || - !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) || - !std::strncmp(ss,"prod(",5) || - !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) || - !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions - _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'": - ss[3]=='k'?"Function 'argkth()'": - ss[4]=='i'?"Function 'argmin()'": - "Function 'argmax()'"): - *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"): - *ss=='k'?"Function 'kth()'": - *ss=='p'?"Function 'prod()'": - *ss=='v'?"Function 'var()'": - ss[1]=='i'?"Function 'min()'": - ss[1]=='a'?"Function 'max()'":"Function 'med()'"); - op = *ss=='a'?(ss[1]=='v'?mp_avg:ss[3]=='k'?mp_argkth:ss[4]=='i'?mp_argmin:mp_argmax): - *ss=='s'?(ss[1]=='u'?mp_sum:mp_std): - *ss=='k'?mp_kth: - *ss=='p'?mp_prod: - *ss=='v'?mp_var: - ss[1]=='i'?mp_min: - ss[1]=='a'?mp_max: - ss[2]=='a'?mp_avg: - mp_median; - is_sth = true; // Tell if all arguments are constant - pos = scalar(); - CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode); - for (s = std::strchr(ss,'(') + 1; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - arg2 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg2)) - CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_size(arg2)). - move_to(l_opcode); - else CImg<ulongT>::vector(arg2).move_to(l_opcode); - is_sth&=_cimg_mp_is_constant(arg2); - s = ns; - } - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - if (is_sth) _cimg_mp_constant(op(*this)); - opcode.move_to(code); - _cimg_mp_return(pos); - } - - // No corresponding built-in function -> Look for a user-defined macro call. - s0 = strchr(ss,'('); - if (s0) { - variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; - - // Count number of specified arguments. - p1 = 0; - for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { - while (*s && cimg::is_blank(*s)) ++s; - if (*s==')' && !p1) break; - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - } - - arg3 = 0; // Number of possible name matches - cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 && - macro_def[l].back()==(char)p1) { - p2 = (unsigned int)macro_def[l].back(); // Number of required arguments - CImg<charT> _expr = macro_body[l]; // Expression to be substituted - - p1 = 1; // Indice of current parsed argument - for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments - while (*s && cimg::is_blank(*s)) ++s; - if (*s==')' && p1==1) break; // Function has no arguments - if (p1>p2) { ++p1; break; } - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; - variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write - arg2 = 0; - cimg_forX(_expr,k) { - if (_expr[k]==(char)p1) { // Perform argument substitution - arg1 = _expr._width; - _expr.resize(arg1 + variable_name._width - 2,1,1,1,0); - std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1); - std::memcpy(_expr._data + k,variable_name,variable_name._width - 1); - k+=variable_name._width - 2; - } - ++arg2; - } - } - - // Recompute 'pexpr' and 'level' for evaluating substituted expression. - CImg<charT> _pexpr(_expr._width); - ns = _pexpr._data; - for (ps = _expr._data, c1 = ' '; *ps; ++ps) { - if (!cimg::is_blank(*ps)) c1 = *ps; - *(ns++) = c1; - } - *ns = 0; - - CImg<uintT> _level = get_level(_expr); - expr.swap(_expr); - pexpr.swap(_pexpr); - level.swap(_level); - s0 = user_macro; - user_macro = macro_def[l]; - pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_single); - user_macro = s0; - level.swap(_level); - pexpr.swap(_pexpr); - expr.swap(_expr); - _cimg_mp_return(pos); - } - - if (arg3) { // Macro name matched but number of arguments does not - CImg<uintT> sig_nargs(arg3); - arg1 = 0; - cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name)) - sig_nargs[arg1++] = (unsigned int)macro_def[l].back(); - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - if (sig_nargs._width>1) { - sig_nargs.sort(); - arg1 = sig_nargs.back(); - --sig_nargs._width; - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " - "does not match macro declaration (defined for %s or %u arguments), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,variable_name._data, - p1,sig_nargs.value_string()._data,arg1, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } else - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " - "does not match macro declaration (defined for %u argument%s), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,variable_name._data, - p1,*sig_nargs,*sig_nargs!=1?"s":"", - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - } - } // if (se1==')') - - // Char / string initializer. - if (*se1=='\'' && - ((se1>ss && *ss=='\'') || - (se1>ss1 && *ss=='_' && *ss1=='\''))) { - if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; } - else { _cimg_mp_op("String initializer"); s1 = ss1; } - arg1 = (unsigned int)(se1 - s1); // Original string length - if (arg1) { - CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0; - cimg::strunescape(variable_name); - arg1 = (unsigned int)std::strlen(variable_name); - } - if (!arg1) _cimg_mp_return(0); // Empty string -> 0 - if (*ss=='_') { - if (arg1==1) _cimg_mp_constant(*variable_name); - *se = saved_char; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Literal %s contains more than one character, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - ss1, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(arg1); - CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); - CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); - std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); - (l_opcode>'y').move_to(code); - _cimg_mp_return(pos); - } - - // Vector initializer [ ... ]. - if (*ss=='[' && *se1==']') { - _cimg_mp_op("Vector initializer"); - s1 = ss1; while (s1<se2 && cimg::is_blank(*s1)) ++s1; - s2 = se2; while (s2>s1 && cimg::is_blank(*s2)) --s2; - if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string - arg1 = (unsigned int)(s2 - s1 - 1); // Original string length - if (arg1) { - CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0; - cimg::strunescape(variable_name); - arg1 = (unsigned int)std::strlen(variable_name); - } - if (!arg1) _cimg_mp_return(0); // Empty string -> 0 - pos = vector(arg1); - CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); - CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); - std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); - (l_opcode>'y').move_to(code); - } else { // Vector values provided as list of items - arg1 = 0; // Number of specified values - if (*ss1!=']') for (s = ss1; s<se; ++s) { - ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) && - (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns; - arg2 = compile(s,ns,depth1,0,is_single); - if (_cimg_mp_is_vector(arg2)) { - arg3 = _cimg_mp_size(arg2); - CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode); - arg1+=arg3; - } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; } - s = ns; - } - if (!arg1) _cimg_mp_return(0); - pos = vector(arg1); - l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0); - (l_opcode>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - } - _cimg_mp_return(pos); - } - - // Variables related to the input list of images. - if (*ss1=='#' && ss2<se) { - arg1 = compile(ss2,se,depth1,0,is_single); - p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U); - switch (*ss) { - case 'w' : // w#ind - if (!listin) _cimg_mp_return(0); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._width); - _cimg_mp_scalar1(mp_list_width,arg1); - case 'h' : // h#ind - if (!listin) _cimg_mp_return(0); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._height); - _cimg_mp_scalar1(mp_list_height,arg1); - case 'd' : // d#ind - if (!listin) _cimg_mp_return(0); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._depth); - _cimg_mp_scalar1(mp_list_depth,arg1); - case 'r' : // r#ind - if (!listin) _cimg_mp_return(0); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._is_shared); - _cimg_mp_scalar1(mp_list_is_shared,arg1); - case 's' : // s#ind - if (!listin) _cimg_mp_return(0); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._spectrum); - _cimg_mp_scalar1(mp_list_spectrum,arg1); - case 'i' : // i#ind - if (!listin) _cimg_mp_return(0); - _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c, - 0,_cimg_mp_boundary); - case 'I' : // I#ind - p2 = p1!=~0U?listin[p1]._spectrum:listin._width?~0U:0; - if (!p2) _cimg_mp_return(0); - pos = vector(p2); - CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code); - _cimg_mp_return(pos); - case 'R' : // R#ind - if (!listin) _cimg_mp_return(0); - _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0, - 0,_cimg_mp_boundary); - case 'G' : // G#ind - if (!listin) _cimg_mp_return(0); - _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1, - 0,_cimg_mp_boundary); - case 'B' : // B#ind - if (!listin) _cimg_mp_return(0); - _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2, - 0,_cimg_mp_boundary); - case 'A' : // A#ind - if (!listin) _cimg_mp_return(0); - _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3, - 0,_cimg_mp_boundary); - } - } - - if (*ss1 && *ss2=='#' && ss3<se) { - arg1 = compile(ss3,se,depth1,0,is_single); - p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U); - if (*ss=='w' && *ss1=='h') { // wh#ind - if (!listin) _cimg_mp_return(0); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height); - _cimg_mp_scalar1(mp_list_wh,arg1); - } - arg2 = ~0U; - - if (*ss=='i') { - if (*ss1=='c') { // ic#ind - if (!listin) _cimg_mp_return(0); - if (_cimg_mp_is_constant(arg1)) { - if (!list_median) list_median.assign(listin._width); - if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]); - _cimg_mp_constant(*list_median[p1]); - } - _cimg_mp_scalar1(mp_list_median,arg1); - } - if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind - if (!listin) _cimg_mp_return(0); - _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0', - 0,_cimg_mp_boundary); - } - switch (*ss1) { - case 'm' : arg2 = 0; break; // im#ind - case 'M' : arg2 = 1; break; // iM#ind - case 'a' : arg2 = 2; break; // ia#ind - case 'v' : arg2 = 3; break; // iv#ind - case 's' : arg2 = 12; break; // is#ind - case 'p' : arg2 = 13; break; // ip#ind - } - } else if (*ss1=='m') switch (*ss) { - case 'x' : arg2 = 4; break; // xm#ind - case 'y' : arg2 = 5; break; // ym#ind - case 'z' : arg2 = 6; break; // zm#ind - case 'c' : arg2 = 7; break; // cm#ind - } else if (*ss1=='M') switch (*ss) { - case 'x' : arg2 = 8; break; // xM#ind - case 'y' : arg2 = 9; break; // yM#ind - case 'z' : arg2 = 10; break; // zM#ind - case 'c' : arg2 = 11; break; // cM#ind - } - if (arg2!=~0U) { - if (!listin) _cimg_mp_return(0); - if (_cimg_mp_is_constant(arg1)) { - if (!list_stats) list_stats.assign(listin._width); - if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false); - _cimg_mp_constant(list_stats(p1,arg2)); - } - _cimg_mp_scalar2(mp_list_stats,arg1,arg2); - } - } - - if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind - arg1 = compile(ss4,se,depth1,0,is_single); - if (!listin) _cimg_mp_return(0); - p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth); - _cimg_mp_scalar1(mp_list_whd,arg1); - } - if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind - arg1 = compile(ss5,se,depth1,0,is_single); - if (!listin) _cimg_mp_return(0); - p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U); - if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth*listin[p1]._spectrum); - _cimg_mp_scalar1(mp_list_whds,arg1); - } - - if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation - if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary - - // No known item found, assuming this is an already initialized variable. - variable_name.assign(ss,(unsigned int)(se - ss + 1)).back() = 0; - if (variable_name[1]) { // Multi-char variable - cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i])) - _cimg_mp_return(variable_pos[i]); - } else if (reserved_label[*variable_name]!=~0U) // Single-char variable - _cimg_mp_return(reserved_label[*variable_name]); - - // Reached an unknown item -> error. - is_sth = true; // is_valid_variable_name - if (*variable_name>='0' && *variable_name<='9') is_sth = false; - else for (ns = variable_name._data; *ns; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - - *se = saved_char; - c1 = *se1; - cimg::strellipsize(variable_name,64); - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - if (is_sth) - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - s1 = std::strchr(ss,'('); - s_op = s1 && c1==')'?"function call":"item"; - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function, - s_op,variable_name._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - - // Evaluation procedure. - double operator()(const double x, const double y, const double z, const double c) { - mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; - for (p_code = code; p_code<p_code_end; ++p_code) { - opcode._data = p_code->_data; - const ulongT target = opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - return *result; - } - - // Evaluation procedure (return output values in vector 'output'). - template<typename t> - void operator()(const double x, const double y, const double z, const double c, t *const output) { - mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; - for (p_code = code; p_code<p_code_end; ++p_code) { - opcode._data = p_code->_data; - const ulongT target = opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - if (result_dim) { - const double *ptrs = result + 1; - t *ptrd = output; - for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++); - } else *output = (t)*result; - } - - // Evaluation procedure for the end() blocks. - void end() { - if (code_end.is_empty()) return; - if (imgin) { - mem[_cimg_mp_slot_x] = imgin._width - 1.; - mem[_cimg_mp_slot_y] = imgin._height - 1.; - mem[_cimg_mp_slot_z] = imgin._depth - 1.; - mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; - } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; - p_code_end = code_end.end(); - for (p_code = code_end; p_code<p_code_end; ++p_code) { - opcode._data = p_code->_data; - const ulongT target = opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - } - - // Return type of a memory element as a string. - CImg<charT> s_type(const unsigned int arg) const { - CImg<charT> res; - if (_cimg_mp_is_vector(arg)) { // Vector - CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res); - cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg)); - } else CImg<charT>::string("scalar").move_to(res); - return res; - } - - // Insert constant value in memory. - unsigned int constant(const double val) { - - // Search for built-in constant. - if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan; - if (val==(double)(int)val) { - if (val>=0 && val<=10) return (unsigned int)val; - if (val<0 && val>=-5) return (unsigned int)(10 - val); - } - if (val==0.5) return 16; - - // Search for constant already requested before (in const cache). - unsigned int ind = ~0U; - if (constcache_size<1024) { - if (!constcache_size) { - constcache_vals.assign(16,1,1,1,0); - constcache_inds.assign(16,1,1,1,0); - *constcache_vals = val; - constcache_size = 1; - ind = 0; - } else { // Dichotomic search - const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1]; - if (val_beg>=val) ind = 0; - else if (val_end==val) ind = constcache_size - 1; - else if (val_end<val) ind = constcache_size; - else { - unsigned int i0 = 1, i1 = constcache_size - 2; - while (i0<=i1) { - const unsigned int mid = (i0 + i1)/2; - if (constcache_vals[mid]==val) { i0 = mid; break; } - else if (constcache_vals[mid]<val) i0 = mid + 1; - else i1 = mid - 1; - } - ind = i0; - } - - if (ind>=constcache_size || constcache_vals[ind]!=val) { - ++constcache_size; - if (constcache_size>constcache_vals._width) { - constcache_vals.resize(-200,1,1,1,0); - constcache_inds.resize(-200,1,1,1,0); - } - const int l = constcache_size - (int)ind - 1; - if (l>0) { - std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double)); - std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int)); - } - constcache_vals[ind] = val; - constcache_inds[ind] = 0; - } - } - if (constcache_inds[ind]) return constcache_inds[ind]; - } - - // Insert new constant in memory if necessary. - if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); } - const unsigned int pos = mempos++; - mem[pos] = val; - memtype[pos] = 1; // Set constant property - if (ind!=~0U) constcache_inds[ind] = pos; - return pos; - } - - // Insert code instructions for processing scalars. - unsigned int scalar() { // Insert new scalar in memory - if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); } - return mempos++; - } - - unsigned int scalar0(const mp_func op) { - const unsigned int pos = scalar(); - CImg<ulongT>::vector((ulongT)op,pos).move_to(code); - return pos; - } - - unsigned int scalar1(const mp_func op, const unsigned int arg1) { - const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:scalar(); - CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code); - return pos; - } - - unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: - arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:scalar(); - CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code); - return pos; - } - - unsigned int scalar3(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { - const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: - arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: - arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:scalar(); - CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code); - return pos; - } - - unsigned int scalar4(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, - const unsigned int arg4) { - const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: - arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: - arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: - arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:scalar(); - CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code); - return pos; - } - - unsigned int scalar5(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, - const unsigned int arg4, const unsigned int arg5) { - const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: - arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: - arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: - arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: - arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:scalar(); - CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code); - return pos; - } - - unsigned int scalar6(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, - const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) { - const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: - arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: - arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: - arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: - arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: - arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:scalar(); - CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); - return pos; - } - - unsigned int scalar7(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, - const unsigned int arg4, const unsigned int arg5, const unsigned int arg6, - const unsigned int arg7) { - const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: - arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: - arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: - arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: - arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: - arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: - arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:scalar(); - CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); - return pos; - } - - // Return a string that defines the calling function + the user-defined function scope. - CImg<charT> calling_function_s() const { - CImg<charT> res; - const unsigned int - l1 = calling_function?(unsigned int)std::strlen(calling_function):0U, - l2 = user_macro?(unsigned int)std::strlen(user_macro):0U; - if (l2) { - res.assign(l1 + l2 + 48); - cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro); - } else { - res.assign(l1 + l2 + 4); - cimg_snprintf(res,res._width,"%s()",calling_function); - } - return res; - } - - // Return true if specified argument can be a part of an allowed variable name. - bool is_varchar(const char c) const { - return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; - } - - // Insert code instructions for processing vectors. - bool is_comp_vector(const unsigned int arg) const { - unsigned int siz = _cimg_mp_size(arg); - if (siz>8) return false; - const int *ptr = memtype.data(arg + 1); - bool is_tmp = true; - while (siz-->0) if (*(ptr++)) { is_tmp = false; break; } - return is_tmp; - } - - void set_variable_vector(const unsigned int arg) { - unsigned int siz = _cimg_mp_size(arg); - int *ptr = memtype.data(arg + 1); - while (siz-->0) *(ptr++) = -1; - } - - unsigned int vector(const unsigned int siz) { // Insert new vector of specified size in memory - if (mempos + siz>=mem._width) { - mem.resize(2*mem._width + siz,1,1,1,0); - memtype.resize(mem._width,1,1,1,0); - } - const unsigned int pos = mempos++; - mem[pos] = cimg::type<double>::nan(); - memtype[pos] = siz + 1; - mempos+=siz; - return pos; - } - - unsigned int vector(const unsigned int siz, const double value) { // Insert new initialized vector - const unsigned int pos = vector(siz); - double *ptr = &mem[pos] + 1; - for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value; - return pos; - } - - unsigned int vector_copy(const unsigned int arg) { // Insert new copy of specified vector in memory - const unsigned int - siz = _cimg_mp_size(arg), - pos = vector(siz); - CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code); - return pos; - } - - void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) { - const unsigned int siz = _cimg_mp_size(pos); - if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]); - } - } - - void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) { - const unsigned int siz = _cimg_mp_size(pos); - if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); - } - } - - unsigned int vector1_v(const mp_func op, const unsigned int arg1) { - const unsigned int - siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int - siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz); - if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int - siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - const unsigned int - siz = _cimg_mp_size(arg2), - pos = is_comp_vector(arg2)?arg2:vector(siz); - if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2, - const unsigned int arg3) { - const unsigned int - siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code); - else { - code.insert(siz); - for (unsigned int k = 1; k<=siz; ++k) - CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]); - } - return pos; - } - - // Check if a memory slot is a positive integer constant scalar value. - // 'mode' can be: - // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant } - void check_constant(const unsigned int arg, const unsigned int n_arg, - const unsigned int mode, - char *const ss, char *const se, const char saved_char) { - _cimg_mp_check_type(arg,n_arg,1,0); - if (!(_cimg_mp_is_constant(arg) && - (!mode || (double)(int)mem[arg]==mem[arg]) && - (mode<2 || mem[arg]>=(mode==3)))) { - const char *s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ": - n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth ": - n_arg==9?"Ninth ":"One of the "; - *se = saved_char; - char *const s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - s_arg,*s_arg?"argument":"Argument",s_type(arg)._data, - !mode?"":mode==1?"n integer": - mode==2?" positive integer":" strictly positive integer", - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - } - - // Check a matrix is square. - void check_matrix_square(const unsigned int arg, const unsigned int n_arg, - char *const ss, char *const se, const char saved_char) { - _cimg_mp_check_type(arg,n_arg,2,0); - const unsigned int - siz = _cimg_mp_size(arg), - n = (unsigned int)cimg::round(std::sqrt((float)siz)); - if (n*n!=siz) { - const char *s_arg; - if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand "; - else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One "; - *se = saved_char; - char *const s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s%s %s%s (of type '%s') " - "cannot be considered as a square matrix, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"), - s_type(arg)._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - } - - // Check type compatibility for one argument. - // Bits of 'mode' tells what types are allowed: - // { 1 = scalar | 2 = vectorN }. - // If 'N' is not zero, it also restricts the vectors to be of size N only. - void check_type(const unsigned int arg, const unsigned int n_arg, - const unsigned int mode, const unsigned int N, - char *const ss, char *const se, const char saved_char) { - const bool - is_scalar = _cimg_mp_is_scalar(arg), - is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N); - bool cond = false; - if (mode&1) cond|=is_scalar; - if (mode&2) cond|=is_vector; - if (!cond) { - const char *s_arg; - if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand "; - else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ": - n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth": - n_arg==9?"Ninth":"One of the "; - CImg<charT> sb_type(32); - if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'"); - else if (mode==2) { - if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N); - else cimg_snprintf(sb_type,sb_type._width,"'vector'"); - } else { - if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N); - else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'"); - } - *se = saved_char; - char *const s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"), - s_type(arg)._data,sb_type._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - } - - // Check that listin or listout are not empty. - void check_list(const bool is_out, - char *const ss, char *const se, const char saved_char) { - if ((!is_out && !listin) || (is_out && !listout)) { - *se = saved_char; - char *const s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s%s Invalid call with an empty image list, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - } - - // Evaluation functions, known by the parser. - // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT), - // so we can store pointers to them directly in the opcode vectors. -#ifdef _mp_arg -#undef _mp_arg -#endif -#define _mp_arg(x) mp.mem[mp.opcode[x]] - - static double mp_abs(_cimg_math_parser& mp) { - return cimg::abs(_mp_arg(2)); - } - - static double mp_add(_cimg_math_parser& mp) { - return _mp_arg(2) + _mp_arg(3); - } - - static double mp_acos(_cimg_math_parser& mp) { - return std::acos(_mp_arg(2)); - } - - static double mp_acosh(_cimg_math_parser& mp) { - return cimg::acosh(_mp_arg(2)); - } - - static double mp_asinh(_cimg_math_parser& mp) { - return cimg::asinh(_mp_arg(2)); - } - - static double mp_atanh(_cimg_math_parser& mp) { - return cimg::atanh(_mp_arg(2)); - } - - static double mp_arg(_cimg_math_parser& mp) { - const int _ind = (int)_mp_arg(4); - const unsigned int - nb_args = (unsigned int)mp.opcode[2] - 4, - ind = _ind<0?_ind + nb_args:(unsigned int)_ind, - siz = (unsigned int)mp.opcode[3]; - if (siz>0) { - if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double)); - else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double)); - return cimg::type<double>::nan(); - } - if (ind>=nb_args) return 0; - return _mp_arg(ind + 4); - } - - static double mp_argkth(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - const double val = mp_kth(mp); - for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.; - return 1; - } - - static double mp_argmin(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - unsigned int argval = 0; - for (unsigned int i = 4; i<i_end; ++i) { - const double _val = _mp_arg(i); - if (_val<val) { val = _val; argval = i - 3; } - } - return (double)argval; - } - - static double mp_argmax(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - unsigned int argval = 0; - for (unsigned int i = 4; i<i_end; ++i) { - const double _val = _mp_arg(i); - if (_val>val) { val = _val; argval = i - 3; } - } - return (double)argval; - } - - static double mp_asin(_cimg_math_parser& mp) { - return std::asin(_mp_arg(2)); - } - - static double mp_atan(_cimg_math_parser& mp) { - return std::atan(_mp_arg(2)); - } - - static double mp_atan2(_cimg_math_parser& mp) { - return std::atan2(_mp_arg(2),_mp_arg(3)); - } - - static double mp_avg(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i); - return val/(i_end - 3); - } - - static double mp_bitwise_and(_cimg_math_parser& mp) { - return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3)); - } - - static double mp_bitwise_left_shift(_cimg_math_parser& mp) { - return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3)); - } - - static double mp_bitwise_not(_cimg_math_parser& mp) { - // Limit result to 32bits such that it can be entirely represented as a 'double'. - return (double)~(unsigned int)_mp_arg(2); - } - - static double mp_bitwise_or(_cimg_math_parser& mp) { - return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3)); - } - - static double mp_bitwise_right_shift(_cimg_math_parser& mp) { - return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3)); - } - - static double mp_bitwise_xor(_cimg_math_parser& mp) { - return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3)); - } - - static double mp_bool(_cimg_math_parser& mp) { - return (double)(bool)_mp_arg(2); - } - - static double mp_break(_cimg_math_parser& mp) { - mp.break_type = 1; - mp.p_code = mp.p_break - 1; - return cimg::type<double>::nan(); - } - - static double mp_breakpoint(_cimg_math_parser& mp) { - cimg_abort_init; - cimg_abort_test; - cimg::unused(mp); - return cimg::type<double>::nan(); - } - - static double mp_cats(_cimg_math_parser& mp) { - const double *ptrd = &_mp_arg(1) + 1; - const unsigned int - sizd = (unsigned int)mp.opcode[2], - nb_args = (unsigned int)(mp.opcode[3] - 4)/2; - CImgList<charT> _str; - for (unsigned int n = 0; n<nb_args; ++n) { - const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n]; - if (siz) { // Vector argument - const double *ptrs = &_mp_arg(4 + 2*n) + 1; - unsigned int l = 0; - while (l<siz && ptrs[l]) ++l; - CImg<doubleT>(ptrs,l,1,1,1,true).move_to(_str); - } else CImg<charT>::vector((char)_mp_arg(4 + 2*n)).move_to(_str); // Scalar argument - } - CImg(1,1,1,1,0).move_to(_str); - const CImg<charT> str = _str>'x'; - const unsigned int l = std::min(str._width,sizd); - CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1); - return cimg::type<double>::nan(); - } - - static double mp_cbrt(_cimg_math_parser& mp) { - return cimg::cbrt(_mp_arg(2)); - } - - static double mp_ceil(_cimg_math_parser& mp) { - return std::ceil(_mp_arg(2)); - } - - static double mp_complex_abs(_cimg_math_parser& mp) { - return cimg::_hypot(_mp_arg(2),_mp_arg(3)); - } - - static double mp_complex_conj(_cimg_math_parser& mp) { - const double *ptrs = &_mp_arg(2) + 1; - double *ptrd = &_mp_arg(1) + 1; - *(ptrd++) = *(ptrs++); - *ptrd = -*(ptrs); - return cimg::type<double>::nan(); - } - - static double mp_complex_div_sv(_cimg_math_parser& mp) { - const double - *ptr2 = &_mp_arg(3) + 1, - r1 = _mp_arg(2), - r2 = *(ptr2++), i2 = *ptr2; - double *ptrd = &_mp_arg(1) + 1; - const double denom = r2*r2 + i2*i2; - *(ptrd++) = r1*r2/denom; - *ptrd = -r1*i2/denom; - return cimg::type<double>::nan(); - } - - static double mp_complex_div_vv(_cimg_math_parser& mp) { - const double - *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, - r1 = *(ptr1++), i1 = *ptr1, - r2 = *(ptr2++), i2 = *ptr2; - double *ptrd = &_mp_arg(1) + 1; - const double denom = r2*r2 + i2*i2; - *(ptrd++) = (r1*r2 + i1*i2)/denom; - *ptrd = (r2*i1 - r1*i2)/denom; - return cimg::type<double>::nan(); - } - - static double mp_complex_exp(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs), er = std::exp(r); - *(ptrd++) = er*std::cos(i); - *(ptrd++) = er*std::sin(i); - return cimg::type<double>::nan(); - } - - static double mp_complex_log(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs); - *(ptrd++) = 0.5*std::log(r*r + i*i); - *(ptrd++) = std::atan2(i,r); - return cimg::type<double>::nan(); - } - - static double mp_complex_mul(_cimg_math_parser& mp) { - const double - *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, - r1 = *(ptr1++), i1 = *ptr1, - r2 = *(ptr2++), i2 = *ptr2; - double *ptrd = &_mp_arg(1) + 1; - *(ptrd++) = r1*r2 - i1*i2; - *(ptrd++) = r1*i2 + r2*i1; - return cimg::type<double>::nan(); - } - - static void _mp_complex_pow(const double r1, const double i1, - const double r2, const double i2, - double *ptrd) { - double ro, io; - if (cimg::abs(i2)<1e-15) { // Exponent is real - if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) { - if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; } - else ro = io = 0; - } else { - const double - mod1_2 = r1*r1 + i1*i1, - phi1 = std::atan2(i1,r1), - modo = std::pow(mod1_2,0.5*r2), - phio = r2*phi1; - ro = modo*std::cos(phio); - io = modo*std::sin(phio); - } - } else { // Exponent is complex - if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0; - const double - mod1_2 = r1*r1 + i1*i1, - phi1 = std::atan2(i1,r1), - modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1), - phio = r2*phi1 + 0.5*i2*std::log(mod1_2); - ro = modo*std::cos(phio); - io = modo*std::sin(phio); - } - *(ptrd++) = ro; - *ptrd = io; - } - - static double mp_complex_pow_ss(_cimg_math_parser& mp) { - const double val1 = _mp_arg(2), val2 = _mp_arg(3); - double *ptrd = &_mp_arg(1) + 1; - _mp_complex_pow(val1,0,val2,0,ptrd); - return cimg::type<double>::nan(); - } - - static double mp_complex_pow_sv(_cimg_math_parser& mp) { - const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1; - double *ptrd = &_mp_arg(1) + 1; - _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd); - return cimg::type<double>::nan(); - } - - static double mp_complex_pow_vs(_cimg_math_parser& mp) { - const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3); - double *ptrd = &_mp_arg(1) + 1; - _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd); - return cimg::type<double>::nan(); - } - - static double mp_complex_pow_vv(_cimg_math_parser& mp) { - const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1; - double *ptrd = &_mp_arg(1) + 1; - _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd); - return cimg::type<double>::nan(); - } - - static double mp_continue(_cimg_math_parser& mp) { - mp.break_type = 2; - mp.p_code = mp.p_break - 1; - return cimg::type<double>::nan(); - } - - static double mp_cos(_cimg_math_parser& mp) { - return std::cos(_mp_arg(2)); - } - - static double mp_cosh(_cimg_math_parser& mp) { - return std::cosh(_mp_arg(2)); - } - - static double mp_critical(_cimg_math_parser& mp) { - const double res = _mp_arg(1); - cimg_pragma_openmp(critical(mp_critical)) - { - for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2]; - mp.p_code<p_end; ++mp.p_code) { // Evaluate body - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - } - --mp.p_code; - return res; - } - - static double mp_crop(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); - const unsigned int - dx = (unsigned int)mp.opcode[7], - dy = (unsigned int)mp.opcode[8], - dz = (unsigned int)mp.opcode[9], - dc = (unsigned int)mp.opcode[10]; - const unsigned int boundary_conditions = (unsigned int)_mp_arg(11); - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[ind]; - if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); - else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, - x + dx - 1,y + dy - 1, - z + dz - 1,c + dc - 1, - boundary_conditions); - return cimg::type<double>::nan(); - } - - static double mp_cross(_cimg_math_parser& mp) { - CImg<doubleT> - vout(&_mp_arg(1) + 1,1,3,1,1,true), - v1(&_mp_arg(2) + 1,1,3,1,1,true), - v2(&_mp_arg(3) + 1,1,3,1,1,true); - (vout = v1).cross(v2); - return cimg::type<double>::nan(); - } - - static double mp_cut(_cimg_math_parser& mp) { - double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4); - return val<cmin?cmin:val>cmax?cmax:val; - } - - static double mp_date(_cimg_math_parser& mp) { - const unsigned int - _arg = (unsigned int)mp.opcode[3], - _siz = (unsigned int)mp.opcode[4], - siz = _siz?_siz:1; - const double *const arg_in = _arg==~0U?0:&_mp_arg(3) + (_siz?1:0); - double *const arg_out = &_mp_arg(1) + (_siz?1:0); - if (arg_in) std::memcpy(arg_out,arg_in,siz*sizeof(double)); - else for (unsigned int i = 0; i<siz; ++i) arg_out[i] = i; - - CImg<charT> filename(mp.opcode[2] - 5); - if (filename) { - const ulongT *ptrs = mp.opcode._data + 5; - cimg_for(filename,ptrd,char) *ptrd = (char)*(ptrs++); - cimg::fdate(filename,arg_out,siz); - } else cimg::date(arg_out,siz); - return _siz?cimg::type<double>::nan():*arg_out; - } - - static double mp_debug(_cimg_math_parser& mp) { - CImg<charT> expr(mp.opcode[2] - 4); - const ulongT *ptrs = mp.opcode._data + 4; - cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - cimg::strellipsize(expr); - const ulongT g_target = mp.opcode[1]; - -#ifndef cimg_use_openmp - const unsigned int n_thread = 0; -#else - const unsigned int n_thread = omp_get_thread_num(); -#endif - cimg_pragma_openmp(critical(mp_debug)) - { - std::fprintf(cimg::output(), - "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" - "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", - (void*)&mp,n_thread,mp.debug_indent,' ', - expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width); - std::fflush(cimg::output()); - mp.debug_indent+=3; - } - const CImg<ulongT> *const p_end = (++mp.p_code) + mp.opcode[3]; - CImg<ulongT> _op; - for ( ; mp.p_code<p_end; ++mp.p_code) { - const CImg<ulongT> &op = *mp.p_code; - mp.opcode._data = op._data; - - _op.assign(1,op._height - 1); - const ulongT *ptrs = op._data + 1; - for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd) - *ptrd = *(ptrs++); - - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - cimg_pragma_openmp(critical(mp_debug)) - { - std::fprintf(cimg::output(), - "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" - "Opcode %p = [ %p,%s ] -> mem[%u] = %g", - (void*)&mp,n_thread,mp.debug_indent,' ', - (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(), - (unsigned int)target,mp.mem[target]); - std::fflush(cimg::output()); - } - } - cimg_pragma_openmp(critical(mp_debug)) - { - mp.debug_indent-=3; - std::fprintf(cimg::output(), - "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" - "End debugging expression '%s' -> mem[%u] = %g (memsize: %u)", - (void*)&mp,n_thread,mp.debug_indent,' ', - expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width); - std::fflush(cimg::output()); - } - --mp.p_code; - return mp.mem[g_target]; - } - - static double mp_decrement(_cimg_math_parser& mp) { - return _mp_arg(2) - 1; - } - - static double mp_det(_cimg_math_parser& mp) { - const double *ptrs = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode[3]; - return CImg<doubleT>(ptrs,k,k,1,1,true).det(); - } - - static double mp_diag(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3; - double *ptrd = &_mp_arg(1) + 1; - std::memset(ptrd,0,siz*siz*sizeof(double)); - for (unsigned int i = 3; i<i_end; ++i) { *(ptrd++) = _mp_arg(i); ptrd+=siz; } - return cimg::type<double>::nan(); - } - - static double mp_display_memory(_cimg_math_parser& mp) { - cimg::unused(mp); - std::fputc('\n',cimg::output()); - mp.mem.display("[" cimg_appname "_math_parser] Memory snapshot"); - return cimg::type<double>::nan(); - } - - static double mp_display(_cimg_math_parser& mp) { - const unsigned int - _siz = (unsigned int)mp.opcode[3], - siz = _siz?_siz:1; - const double *const ptr = &_mp_arg(1) + (_siz?1:0); - const int - w = (int)_mp_arg(4), - h = (int)_mp_arg(5), - d = (int)_mp_arg(6), - s = (int)_mp_arg(7); - CImg<doubleT> img; - if (w>0 && h>0 && d>0 && s>0) { - if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true); - else img.assign(ptr,siz).resize(w,h,d,s,-1); - } else img.assign(ptr,1,siz,1,1,true); - - CImg<charT> expr(mp.opcode[2] - 8); - const ulongT *ptrs = mp.opcode._data + 8; - cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr); - cimg::strellipsize(expr); - std::fputc('\n',cimg::output()); - img.display(expr._data); - return cimg::type<double>::nan(); - } - - static double mp_div(_cimg_math_parser& mp) { - return _mp_arg(2)/_mp_arg(3); - } - - static double mp_dot(_cimg_math_parser& mp) { - const unsigned int siz = (unsigned int)mp.opcode[4]; - return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true). - dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true)); - } - - static double mp_do(_cimg_math_parser& mp) { - const ulongT - mem_body = mp.opcode[1], - mem_cond = mp.opcode[2]; - const CImg<ulongT> - *const p_body = ++mp.p_code, - *const p_cond = p_body + mp.opcode[3], - *const p_end = p_cond + mp.opcode[4]; - const unsigned int vsiz = (unsigned int)mp.opcode[5]; - if (mp.opcode[6]) { // Set default value for result and condition if necessary - if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan()); - else mp.mem[mem_body] = cimg::type<double>::nan(); - } - if (mp.opcode[7]) mp.mem[mem_cond] = 0; - - const unsigned int _break_type = mp.break_type; - mp.break_type = 0; - do { - for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; - for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; - } while (mp.mem[mem_cond]); - mp.break_type = _break_type; - mp.p_code = p_end - 1; - return mp.mem[mem_body]; - } - - static double mp_draw(_cimg_math_parser& mp) { - const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); - unsigned int ind = (unsigned int)mp.opcode[3]; - - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); - CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - unsigned int - dx = (unsigned int)mp.opcode[8], - dy = (unsigned int)mp.opcode[9], - dz = (unsigned int)mp.opcode[10], - dc = (unsigned int)mp.opcode[11]; - dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); - dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); - dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); - dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); - - const ulongT sizS = mp.opcode[2]; - if (sizS<(ulongT)dx*dy*dz*dc) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " - "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " - "(%lu values) do not match.", - mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); - CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); - const float opacity = (float)_mp_arg(12); - - if (img._data) { - if (mp.opcode[13]!=~0U) { // Opacity mask specified - const ulongT sizM = mp.opcode[14]; - if (sizM<(ulongT)dx*dy*dz) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " - "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " - "(%lu values) do not match.", - mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); - const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); - img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); - } else img.draw_image(x,y,z,c,S,opacity); - } - return cimg::type<double>::nan(); - } - - static double mp_echo(_cimg_math_parser& mp) { - const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; - CImgList<charT> _str; - CImg<charT> it; - for (unsigned int n = 0; n<nb_args; ++n) { - const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n]; - if (siz) { // Vector argument -> string - const double *ptr = &_mp_arg(3 + 2*n) + 1; - unsigned int l = 0; - while (l<siz && ptr[l]) ++l; - CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str); - } else { // Scalar argument -> number - it.assign(256); - cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); - CImg<charT>::string(it,false,true).move_to(_str); - } - } - CImg(1,1,1,1,0).move_to(_str); - const CImg<charT> str = _str>'x'; - std::fprintf(cimg::output(),"\n%s",str._data); - return cimg::type<double>::nan(); - } - - static double mp_ellipse(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - unsigned int ind = (unsigned int)mp.opcode[3]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); - CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - CImg<T> color(img._spectrum,1,1,1,0); - bool is_invalid_arguments = false; - unsigned int i = 4; - float r1 = 0, r2 = 0, angle = 0, opacity = 1; - int x0 = 0, y0 = 0; - if (i>=i_end) is_invalid_arguments = true; - else { - x0 = (int)cimg::round(_mp_arg(i++)); - if (i>=i_end) is_invalid_arguments = true; - else { - y0 = (int)cimg::round(_mp_arg(i++)); - if (i>=i_end) is_invalid_arguments = true; - else { - r1 = (float)_mp_arg(i++); - if (i>=i_end) r2 = r1; - else { - r2 = (float)_mp_arg(i++); - if (i<i_end) { - angle = (float)_mp_arg(i++); - if (i<i_end) { - opacity = (float)_mp_arg(i++); - if (i<i_end) { - cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++); - else { color.resize(k,1,1,1,-1); break; } - color.resize(img._spectrum,1,1,1,0,2); - } - } - } - } - } - } - } - if (!is_invalid_arguments) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity); - else { - CImg<doubleT> args(i_end - 4); - cimg_forX(args,k) args[k] = _mp_arg(4 + k); - if (ind==~0U) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " - "Invalid arguments '%s'. ", - mp.imgin.pixel_type(),args.value_string()._data); - else - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " - "Invalid arguments '#%u%s%s'. ", - mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); - } - return cimg::type<double>::nan(); - } - - static double mp_eq(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)==_mp_arg(3)); - } - - static double mp_ext(_cimg_math_parser& mp) { - const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; - CImgList<charT> _str; - CImg<charT> it; - for (unsigned int n = 0; n<nb_args; ++n) { - const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n]; - if (siz) { // Vector argument -> string - const double *ptr = &_mp_arg(3 + 2*n) + 1; - unsigned int l = 0; - while (l<siz && ptr[l]) ++l; - CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str); - } else { // Scalar argument -> number - it.assign(256); - cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); - CImg<charT>::string(it,false,true).move_to(_str); - } - } - CImg(1,1,1,1,0).move_to(_str); - CImg<charT> str = _str>'x'; -#ifdef cimg_mp_ext_function - cimg_mp_ext_function(str); -#endif - return cimg::type<double>::nan(); - } - - static double mp_exp(_cimg_math_parser& mp) { - return std::exp(_mp_arg(2)); - } - - static double mp_eye(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int k = (unsigned int)mp.opcode[2]; - CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix(); - return cimg::type<double>::nan(); - } - - static double mp_factorial(_cimg_math_parser& mp) { - return cimg::factorial((int)_mp_arg(2)); - } - - static double mp_fibonacci(_cimg_math_parser& mp) { - return cimg::fibonacci((int)_mp_arg(2)); - } - - static double mp_find(_cimg_math_parser& mp) { - const bool is_forward = (bool)_mp_arg(5); - const ulongT siz = (ulongT)mp.opcode[3]; - longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz - 1); - if (ind<0 || ind>=(longT)siz) return -1.; - const double - *const ptrb = &_mp_arg(2) + 1, - *const ptre = ptrb + siz, - val = _mp_arg(4), - *ptr = ptrb + ind; - - // Forward search - if (is_forward) { - while (ptr<ptre && *ptr!=val) ++ptr; - return ptr==ptre?-1.:(double)(ptr - ptrb); - } - - // Backward search. - while (ptr>=ptrb && *ptr!=val) --ptr; - return ptr<ptrb?-1.:(double)(ptr - ptrb); - } - - static double mp_find_seq(_cimg_math_parser& mp) { - const bool is_forward = (bool)_mp_arg(6); - const ulongT - siz1 = (ulongT)mp.opcode[3], - siz2 = (ulongT)mp.opcode[5]; - longT ind = (longT)(mp.opcode[7]!=_cimg_mp_slot_nan?_mp_arg(7):is_forward?0:siz1 - 1); - if (ind<0 || ind>=(longT)siz1) return -1.; - const double - *const ptr1b = &_mp_arg(2) + 1, - *const ptr1e = ptr1b + siz1, - *const ptr2b = &_mp_arg(4) + 1, - *const ptr2e = ptr2b + siz2, - *ptr1 = ptr1b + ind, - *p1 = 0, - *p2 = 0; - - // Forward search. - if (is_forward) { - do { - while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1; - if (ptr1>=ptr1e) return -1.; - p1 = ptr1 + 1; - p2 = ptr2b + 1; - while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; } - } while (p2<ptr2e && ++ptr1<ptr1e); - return p2<ptr2e?-1.:(double)(ptr1 - ptr1b); - } - - // Backward search. - do { - while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1; - if (ptr1<ptr1b) return -1.; - p1 = ptr1 + 1; - p2 = ptr2b + 1; - while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; } - } while (p2<ptr2e && --ptr1>=ptr1b); - return p2<ptr2e?-1.:(double)(ptr1 - ptr1b); - } - - static double mp_floor(_cimg_math_parser& mp) { - return std::floor(_mp_arg(2)); - } - - static double mp_for(_cimg_math_parser& mp) { - const ulongT - mem_body = mp.opcode[1], - mem_cond = mp.opcode[3]; - const CImg<ulongT> - *const p_init = ++mp.p_code, - *const p_cond = p_init + mp.opcode[4], - *const p_body = p_cond + mp.opcode[5], - *const p_post = p_body + mp.opcode[6], - *const p_end = p_post + mp.opcode[7]; - const unsigned int vsiz = (unsigned int)mp.opcode[2]; - bool is_cond = false; - if (mp.opcode[8]) { // Set default value for result and condition if necessary - if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan()); - else mp.mem[mem_body] = cimg::type<double>::nan(); - } - if (mp.opcode[9]) mp.mem[mem_cond] = 0; - const unsigned int _break_type = mp.break_type; - mp.break_type = 0; - - for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - - if (!mp.break_type) do { - for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.break_type==1) break; - - is_cond = (bool)mp.mem[mem_cond]; - if (is_cond && !mp.break_type) { - for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; - - for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; - } - } while (is_cond); - - mp.break_type = _break_type; - mp.p_code = p_end - 1; - return mp.mem[mem_body]; - } - - static double mp_fsize(_cimg_math_parser& mp) { - const CImg<charT> filename(mp.opcode._data + 3,mp.opcode[2] - 3); - return (double)cimg::fsize(filename); - } - - static double mp_g(_cimg_math_parser& mp) { - cimg::unused(mp); - return cimg::grand(&mp.rng); - } - - static double mp_gauss(_cimg_math_parser& mp) { - const double x = _mp_arg(2), s = _mp_arg(3); - return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1); - } - - static double mp_gcd(_cimg_math_parser& mp) { - return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3)); - } - - static double mp_gt(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)>_mp_arg(3)); - } - - static double mp_gte(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)>=_mp_arg(3)); - } - - static double mp_i(_cimg_math_parser& mp) { - return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y], - (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0); - } - - static double mp_if(_cimg_math_parser& mp) { - const bool is_cond = (bool)_mp_arg(2); - const ulongT - mem_left = mp.opcode[3], - mem_right = mp.opcode[4]; - const CImg<ulongT> - *const p_right = ++mp.p_code + mp.opcode[5], - *const p_end = p_right + mp.opcode[6]; - const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7]; - if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) { - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) { - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.p_code==mp.p_break) --mp.p_code; - else mp.p_code = p_end - 1; - if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz); - return mp.mem[is_cond?mem_left:mem_right]; - } - - static double mp_image_d(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.depth(); - } - - static double mp_image_display(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); - cimg::mutex(6); - CImg<T> &img = mp.listout[ind]; - CImg<charT> title(256); - std::fputc('\n',cimg::output()); - cimg_snprintf(title,title._width,"[ Image #%u ]",ind); - img.display(title); - cimg::mutex(6,0); - return cimg::type<double>::nan(); - } - - static double mp_image_h(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.height(); - } - - static double mp_image_median(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.median(); - } - - static double mp_image_print(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); - cimg::mutex(6); - CImg<T> &img = mp.listout[ind]; - CImg<charT> title(256); - std::fputc('\n',cimg::output()); - cimg_snprintf(title,title._width,"[ Image #%u ]",ind); - img.print(title); - cimg::mutex(6,0); - return cimg::type<double>::nan(); - } - - static double mp_image_resize(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); - cimg::mutex(6); - CImg<T> &img = mp.listout[ind]; - const double - _w = mp.opcode[3]==~0U?-100:_mp_arg(3), - _h = mp.opcode[4]==~0U?-100:_mp_arg(4), - _d = mp.opcode[5]==~0U?-100:_mp_arg(5), - _s = mp.opcode[6]==~0U?-100:_mp_arg(6); - const unsigned int - w = (unsigned int)(_w>=0?_w:-_w*img.width()/100), - h = (unsigned int)(_h>=0?_h:-_h*img.height()/100), - d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100), - s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100), - interp = (int)_mp_arg(7); - if (mp.is_fill && img._data==mp.imgout._data) { - cimg::mutex(6,0); - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': " - "Cannot both fill and resize image (%u,%u,%u,%u) " - "to new dimensions (%u,%u,%u,%u).", - img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s); - } - const unsigned int - boundary = (int)_mp_arg(8); - const float - cx = (float)_mp_arg(9), - cy = (float)_mp_arg(10), - cz = (float)_mp_arg(11), - cc = (float)_mp_arg(12); - img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc); - cimg::mutex(6,0); - return cimg::type<double>::nan(); - } - - static double mp_image_s(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.spectrum(); - } - - static double mp_image_sort(_cimg_math_parser& mp) { - const bool is_increasing = (bool)_mp_arg(3); - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()), - axis = (unsigned int)_mp_arg(4); - cimg::mutex(6); - CImg<T> &img = mp.listout[ind]; - img.sort(is_increasing, - axis==0 || axis=='x'?'x': - axis==1 || axis=='y'?'y': - axis==2 || axis=='z'?'z': - axis==3 || axis=='c'?'c':0); - cimg::mutex(6,0); - return cimg::type<double>::nan(); - } - - static double mp_image_stats(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind==~0U) CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats(); - else { - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<doubleT>(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats(); - } - return cimg::type<double>::nan(); - } - - static double mp_image_w(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.width(); - } - - static double mp_image_wh(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.width()*img.height(); - } - - static double mp_image_whd(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.width()*img.height()*img.depth(); - } - - static double mp_image_whds(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - return (double)img.width()*img.height()*img.depth()*img.spectrum(); - } - - static double mp_increment(_cimg_math_parser& mp) { - return _mp_arg(2) + 1; - } - - static double mp_int(_cimg_math_parser& mp) { - return (double)(longT)_mp_arg(2); - } - - static double mp_ioff(_cimg_math_parser& mp) { - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3); - const CImg<T> &img = mp.imgin; - const longT - off = (longT)_mp_arg(2), - whds = (longT)img.size(); - if (off>=0 && off<whds) return (double)img[off]; - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whds2 = 2*whds, moff = cimg::mod(off,whds2); - return (double)img[moff<whds?moff:whds2 - moff - 1]; - } - case 2 : // Periodic - return (double)img[cimg::mod(off,whds)]; - case 1 : // Neumann - return (double)img[off<0?0:whds - 1]; - default : // Dirichlet - return 0; - } - return 0; - } - - static double mp_isbool(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - return (double)(val==0. || val==1.); - } - - static double mp_isin(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - const double val = _mp_arg(3); - for (unsigned int i = 4; i<i_end; ++i) - if (val==_mp_arg(i)) return 1.; - return 0.; - } - - static double mp_isinf(_cimg_math_parser& mp) { - return (double)cimg::type<double>::is_inf(_mp_arg(2)); - } - - static double mp_isint(_cimg_math_parser& mp) { - return (double)(cimg::mod(_mp_arg(2),1.)==0); - } - - static double mp_isnan(_cimg_math_parser& mp) { - return (double)cimg::type<double>::is_nan(_mp_arg(2)); - } - - static double mp_ixyzc(_cimg_math_parser& mp) { - const unsigned int - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7); - const CImg<T> &img = mp.imgin; - const double - x = _mp_arg(2), y = _mp_arg(3), - z = _mp_arg(4), c = _mp_arg(5); - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), - mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); - return (double)img(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - case 1 : // Neumann - return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c); - default : // Dirichlet - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), - mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); - return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - case 1 : // Neumann - return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c); - default : // Dirichlet - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0); - } - } - - static double mp_joff(_cimg_math_parser& mp) { - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3); - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const CImg<T> &img = mp.imgin; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), - whds = (longT)img.size(); - if (off>=0 && off<whds) return (double)img[off]; - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whds2 = 2*whds, moff = cimg::mod(off,whds2); - return (double)img[moff<whds?moff:whds2 - moff - 1]; - } - case 2 : // Periodic - return (double)img[cimg::mod(off,whds)]; - case 1 : // Neumann - return (double)img[off<0?0:whds - 1]; - default : // Dirichlet - return 0; - } - return 0; - } - - static double mp_jxyzc(_cimg_math_parser& mp) { - const unsigned int - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7); - const CImg<T> &img = mp.imgin; - const double - ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], - oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], - x = ox + _mp_arg(2), y = oy + _mp_arg(3), - z = oz + _mp_arg(4), c = oc + _mp_arg(5); - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), - mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); - return (double)img(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - case 1 : // Neumann - return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c); - default : // Dirichlet - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), - mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); - return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - case 1 : // Neumann - return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c); - default : // Dirichlet - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0); - } - } - - static double mp_kth(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - CImg<doubleT> vals(i_end - 4); - double *p = vals.data(); - for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i); - int ind = (int)cimg::round(_mp_arg(3)); - if (ind<0) ind+=vals.width() + 1; - ind = std::max(1,std::min(vals.width(),ind)); - return vals.kth_smallest(ind - 1); - } - - static double mp_linear_add(_cimg_math_parser& mp) { - return _mp_arg(2)*_mp_arg(3) + _mp_arg(4); - } - - static double mp_linear_sub_left(_cimg_math_parser& mp) { - return _mp_arg(2)*_mp_arg(3) - _mp_arg(4); - } - - static double mp_linear_sub_right(_cimg_math_parser& mp) { - return _mp_arg(4) - _mp_arg(2)*_mp_arg(3); - } - - static double mp_list_depth(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._depth; - } - - static double mp_list_find(_cimg_math_parser& mp) { - const unsigned int - indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = mp.listin[indi]; - const bool is_forward = (bool)_mp_arg(4); - const ulongT siz = (ulongT)img.size(); - longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):is_forward?0:siz - 1); - if (ind<0 || ind>=(longT)siz) return -1.; - const T - *const ptrb = img.data(), - *const ptre = img.end(), - *ptr = ptrb + ind; - const double val = _mp_arg(3); - - // Forward search - if (is_forward) { - while (ptr<ptre && (double)*ptr!=val) ++ptr; - return ptr==ptre?-1.:(double)(ptr - ptrb); - } - - // Backward search. - while (ptr>=ptrb && (double)*ptr!=val) --ptr; - return ptr<ptrb?-1.:(double)(ptr - ptrb); - } - - static double mp_list_find_seq(_cimg_math_parser& mp) { - const unsigned int - indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg<T> &img = mp.listin[indi]; - const bool is_forward = (bool)_mp_arg(5); - const ulongT - siz1 = (ulongT)img.size(), - siz2 = (ulongT)mp.opcode[4]; - longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz1 - 1); - if (ind<0 || ind>=(longT)siz1) return -1.; - const T - *const ptr1b = img.data(), - *const ptr1e = ptr1b + siz1, - *ptr1 = ptr1b + ind, - *p1 = 0; - const double - *const ptr2b = &_mp_arg(3) + 1, - *const ptr2e = ptr2b + siz2, - *p2 = 0; - - // Forward search. - if (is_forward) { - do { - while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1; - if (ptr1>=ptr1e) return -1.; - p1 = ptr1 + 1; - p2 = ptr2b + 1; - while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; } - } while (p2<ptr2e && ++ptr1<ptr1e); - return p2<ptr2e?-1.:(double)(ptr1 - ptr1b); - } - - // Backward search. - do { - while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1; - if (ptr1<ptr1b) return -1.; - p1 = ptr1 + 1; - p2 = ptr2b + 1; - while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; } - } while (p2<ptr2e && --ptr1>=ptr1b); - return p2<ptr2e?-1.:(double)(ptr1 - ptr1b); - } - - static double mp_list_height(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._height; - } - - static double mp_list_ioff(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4); - const CImg<T> &img = mp.listin[ind]; - const longT - off = (longT)_mp_arg(3), - whds = (longT)img.size(); - if (off>=0 && off<whds) return (double)img[off]; - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whds2 = 2*whds, moff = cimg::mod(off,whds2); - return (double)img[moff<whds?moff:whds2 - moff - 1]; - } - case 2 : // Periodic - return (double)img[cimg::mod(off,whds)]; - case 1 : // Neumann - return (double)img[off<0?0:whds - 1]; - default : // Dirichlet - return 0; - } - return 0; - } - - static double mp_list_is_shared(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._is_shared; - } - - static double mp_list_ixyzc(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(7), - boundary_conditions = (unsigned int)_mp_arg(8); - const CImg<T> &img = mp.listin[ind]; - const double - x = _mp_arg(3), y = _mp_arg(4), - z = _mp_arg(5), c = _mp_arg(6); - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), - mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); - return (double)img(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - case 1 : // Neumann - return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c); - default : // Dirichlet - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), - mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); - return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - case 1 : // Neumann - return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c); - default : // Dirichlet - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0); - } - } - - static double mp_list_joff(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4); - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const CImg<T> &img = mp.listin[ind]; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), - whds = (longT)img.size(); - if (off>=0 && off<whds) return (double)img[off]; - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whds2 = 2*whds, moff = cimg::mod(off,whds2); - return (double)img[moff<whds?moff:whds2 - moff - 1]; - } - case 2 : // Periodic - return (double)img[cimg::mod(off,whds)]; - case 1 : // Neumann - return (double)img[off<0?0:whds - 1]; - default : // Dirichlet - return 0; - } - return 0; - } - - static double mp_list_jxyzc(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(7), - boundary_conditions = (unsigned int)_mp_arg(8); - const CImg<T> &img = mp.listin[ind]; - const double - ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], - oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], - x = ox + _mp_arg(3), y = oy + _mp_arg(4), - z = oz + _mp_arg(5), c = oc + _mp_arg(6); - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), - mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); - return (double)img(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img(cimg::mod((int)x,img.width()), - cimg::mod((int)y,img.height()), - cimg::mod((int)z,img.depth()), - cimg::mod((int)c,img.spectrum())); - case 1 : // Neumann - return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c); - default : // Dirichlet - return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), - mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); - return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1, - my<img.height()?my:h2 - my - 1, - mz<img.depth()?mz:d2 - mz - 1, - mc<img.spectrum()?mc:s2 - mc - 1); - } - case 2 : // Periodic - return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()), - cimg::mod((float)y,(float)img.height()), - cimg::mod((float)z,(float)img.depth()), - cimg::mod((float)c,(float)img.spectrum())); - case 1 : // Neumann - return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c); - default : // Dirichlet - return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0); - } - } - - static double mp_list_l(_cimg_math_parser& mp) { - return (double)mp.listout.width(); - } - - static double mp_list_median(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - if (!mp.list_median) mp.list_median.assign(mp.listin._width); - if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]); - return *mp.list_median[ind]; - } - - static double mp_list_set_ioff(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const longT - off = (longT)_mp_arg(3), - whds = (longT)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off<whds) img[off] = (T)val; - return val; - } - - static double mp_list_set_ixyzc(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const int - x = (int)_mp_arg(3), y = (int)_mp_arg(4), - z = (int)_mp_arg(5), c = (int)_mp_arg(6); - const double val = _mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && - z>=0 && z<img.depth() && c>=0 && c<img.spectrum()) - img(x,y,z,c) = (T)val; - return val; - } - - static double mp_list_set_joff(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), - whds = (longT)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off<whds) img[off] = (T)val; - return val; - } - - static double mp_list_set_jxyzc(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const double - ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], - oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; - const int - x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), - z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6)); - const double val = _mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && - z>=0 && z<img.depth() && c>=0 && c<img.spectrum()) - img(x,y,z,c) = (T)val; - return val; - } - - static double mp_list_set_Ioff_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const longT - off = (longT)_mp_arg(3), - whd = (longT)img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off<whd) { - T *ptrd = &img[off]; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_list_set_Ioff_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const longT - off = (longT)_mp_arg(3), - whd = (longT)img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off<whd) { - const unsigned int vsiz = (unsigned int)mp.opcode[4]; - T *ptrd = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const int - x = (int)_mp_arg(3), - y = (int)_mp_arg(4), - z = (int)_mp_arg(5); - const T val = (T)_mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const int - x = (int)_mp_arg(3), - y = (int)_mp_arg(4), - z = (int)_mp_arg(5); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - const unsigned int vsiz = (unsigned int)mp.opcode[6]; - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_list_set_Joff_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), - whd = (longT)img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off<whd) { - T *ptrd = &img[off]; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_list_set_Joff_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), - whd = (longT)img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off<whd) { - const unsigned int vsiz = (unsigned int)mp.opcode[4]; - T *ptrd = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; - const int - x = (int)(ox + _mp_arg(3)), - y = (int)(oy + _mp_arg(4)), - z = (int)(oz + _mp_arg(5)); - const T val = (T)_mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg<T> &img = mp.listout[ind]; - const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; - const int - x = (int)(ox + _mp_arg(3)), - y = (int)(oy + _mp_arg(4)), - z = (int)(oz + _mp_arg(5)); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - const unsigned int vsiz = (unsigned int)mp.opcode[6]; - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_list_spectrum(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._spectrum; - } - - static double mp_list_stats(_cimg_math_parser& mp) { - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - k = (unsigned int)mp.opcode[3]; - if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); - if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); - return mp.list_stats(ind,k); - } - - static double mp_list_wh(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height; - } - - static double mp_list_whd(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth; - } - - static double mp_list_whds(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum; - } - - static double mp_list_width(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width; - } - - static double mp_list_Ioff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4), - vsiz = (unsigned int)mp.opcode[5]; - const CImg<T> &img = mp.listin[ind]; - const longT - off = (longT)_mp_arg(3), - whd = (longT)img.width()*img.height()*img.depth(); - const T *ptrs; - if (off>=0 && off<whd) { - ptrs = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); - ptrs = &img[moff<whd?moff:whd2 - moff - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - case 2 : // Periodic - ptrs = &img[cimg::mod(off,whd)]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - case 1 : // Neumann - ptrs = off<0?&img[0]:&img[whd - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - default : // Dirichlet - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - - static double mp_list_Ixyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7), - vsiz = (unsigned int)mp.opcode[8]; - const CImg<T> &img = mp.listin[ind]; - const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - const T *ptrs; - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 2 : { // Periodic - const int - cx = cimg::mod((int)x,img.width()), - cy = cimg::mod((int)y,img.height()), - cz = cimg::mod((int)z,img.depth()); - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 1 : { // Neumann - ptrs = &img._atXYZ((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - default : // Dirichlet - if (img.containsXYZC((int)x,(int)y,(int)z)) { - ptrs = &img((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,vsiz*sizeof(double)); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 2 : { // Periodic - const float - cx = cimg::mod((float)x,(float)img.width()), - cy = cimg::mod((float)y,(float)img.height()), - cz = cimg::mod((float)z,(float)img.depth()); - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 1 : // Neumann - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c); - break; - case 0 : // Dirichlet - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0); - } - return cimg::type<double>::nan(); - } - - static double mp_list_Joff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - boundary_conditions = (unsigned int)_mp_arg(4), - vsiz = (unsigned int)mp.opcode[5]; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z]; - const CImg<T> &img = mp.listin[ind]; - const longT - off = img.offset(ox,oy,oz) + (longT)_mp_arg(3), - whd = (longT)img.width()*img.height()*img.depth(); - const T *ptrs; - if (off>=0 && off<whd) { - ptrs = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); - ptrs = &img[moff<whd?moff:whd2 - moff - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - case 2 : // Periodic - ptrs = &img[cimg::mod(off,whd)]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - case 1 : // Neumann - ptrs = off<0?&img[0]:&img[whd - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - default : // Dirichlet - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - - static double mp_list_Jxyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - interpolation = (unsigned int)_mp_arg(6), - boundary_conditions = (unsigned int)_mp_arg(7), - vsiz = (unsigned int)mp.opcode[8]; - const CImg<T> &img = mp.listin[ind]; - const double - ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], - x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - const T *ptrs; - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 2 : { // Periodic - const int - cx = cimg::mod((int)x,img.width()), - cy = cimg::mod((int)y,img.height()), - cz = cimg::mod((int)z,img.depth()); - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 1 : { // Neumann - ptrs = &img._atXYZ((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - default : // Dirichlet - if (img.containsXYZC((int)x,(int)y,(int)z)) { - ptrs = &img((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,vsiz*sizeof(double)); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 2 : { // Periodic - const float - cx = cimg::mod((float)x,(float)img.width()), - cy = cimg::mod((float)y,(float)img.height()), - cz = cimg::mod((float)z,(float)img.depth()); - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 1 : // Neumann - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c); - break; - default : // Dirichlet - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0); - } - return cimg::type<double>::nan(); - } - - static double mp_log(_cimg_math_parser& mp) { - return std::log(_mp_arg(2)); - } - - static double mp_log10(_cimg_math_parser& mp) { - return std::log10(_mp_arg(2)); - } - - static double mp_log2(_cimg_math_parser& mp) { - return cimg::log2(_mp_arg(2)); - } - - static double mp_logical_and(_cimg_math_parser& mp) { - const bool val_left = (bool)_mp_arg(2); - const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4]; - if (!val_left) { mp.p_code = p_end - 1; return 0; } - const ulongT mem_right = mp.opcode[3]; - for ( ; mp.p_code<p_end; ++mp.p_code) { - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - --mp.p_code; - return (double)(bool)mp.mem[mem_right]; - } - - static double mp_logical_not(_cimg_math_parser& mp) { - return (double)!_mp_arg(2); - } - - static double mp_logical_or(_cimg_math_parser& mp) { - const bool val_left = (bool)_mp_arg(2); - const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4]; - if (val_left) { mp.p_code = p_end - 1; return 1; } - const ulongT mem_right = mp.opcode[3]; - for ( ; mp.p_code<p_end; ++mp.p_code) { - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - --mp.p_code; - return (double)(bool)mp.mem[mem_right]; - } - - static double mp_lowercase(_cimg_math_parser& mp) { - return cimg::lowercase(_mp_arg(2)); - } - - static double mp_lt(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)<_mp_arg(3)); - } - - static double mp_lte(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)<=_mp_arg(3)); - } - - static double mp_matrix_eig(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode[3]; - CImg<doubleT> val, vec; - CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); - CImg<doubleT>(ptrd,1,k,1,1,true) = val; - CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose(); - return cimg::type<double>::nan(); - } - - static double mp_matrix_inv(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode[3]; - CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptr1,k,k,1,1,true).get_invert(); - return cimg::type<double>::nan(); - } - - static double mp_matrix_mul(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double - *ptr1 = &_mp_arg(2) + 1, - *ptr2 = &_mp_arg(3) + 1; - const unsigned int - k = (unsigned int)mp.opcode[4], - l = (unsigned int)mp.opcode[5], - m = (unsigned int)mp.opcode[6]; - CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true); - return cimg::type<double>::nan(); - } - - static double mp_matrix_pseudoinv(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int - k = (unsigned int)mp.opcode[3], - l = (unsigned int)mp.opcode[4]; - CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptr1,k,l,1,1,true).get_pseudoinvert(); - return cimg::type<double>::nan(); - } - - static double mp_matrix_svd(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int - k = (unsigned int)mp.opcode[3], - l = (unsigned int)mp.opcode[4]; - CImg<doubleT> U, S, V; - CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V); - CImg<doubleT>(ptrd,k,l,1,1,true) = U; - CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S; - CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V; - return cimg::type<double>::nan(); - } - - static double mp_max(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i)); - return val; - } - - static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref, - const longT siz, const long inc) { - const longT - off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind, - eoff = off + (siz - 1)*inc; - if (off<0 || eoff>=mp.mem.width()) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " - "Out-of-bounds variable pointer " - "(length: %ld, increment: %ld, offset start: %ld, " - "offset end: %ld, offset max: %u).", - mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1); - return &mp.mem[off]; - } - - static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref, - const longT siz, const long inc) { - const unsigned ind = (unsigned int)p_ref[1]; - const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]; - const bool is_relative = (bool)p_ref[2]; - int ox, oy, oz, oc; - longT off = 0; - if (is_relative) { - ox = (int)mp.mem[_cimg_mp_slot_x]; - oy = (int)mp.mem[_cimg_mp_slot_y]; - oz = (int)mp.mem[_cimg_mp_slot_z]; - oc = (int)mp.mem[_cimg_mp_slot_c]; - off = img.offset(ox,oy,oz,oc); - } - if ((*p_ref)%2) { - const int - x = (int)mp.mem[p_ref[3]], - y = (int)mp.mem[p_ref[4]], - z = (int)mp.mem[p_ref[5]], - c = *p_ref==5?0:(int)mp.mem[p_ref[6]]; - off+=img.offset(x,y,z,c); - } else off+=(longT)mp.mem[p_ref[3]]; - const longT eoff = off + (siz - 1)*inc; - if (off<0 || eoff>=(longT)img.size()) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " - "Out-of-bounds image pointer " - "(length: %ld, increment: %ld, offset start: %ld, " - "offset end: %ld, offset max: %lu).", - mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1); - return (float*)&img[off]; - } - - static double mp_memcopy(_cimg_math_parser& mp) { - longT siz = (longT)_mp_arg(4); - const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6); - const float - _opacity = (float)_mp_arg(7), - opacity = (float)cimg::abs(_opacity), - omopacity = 1 - std::max(_opacity,0.f); - if (siz>0) { - const bool - is_doubled = mp.opcode[8]<=1, - is_doubles = mp.opcode[15]<=1; - if (is_doubled && is_doubles) { // (double*) <- (double*) - double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); - const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); - if (inc_d==1 && inc_s==1 && _opacity>=1) { - if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double)); - else std::memmove(ptrd,ptrs,siz*sizeof(double)); - } else { - if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) { - if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } - else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } - } else { // Overlapping buffers - CImg<doubleT> buf((unsigned int)siz); - cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; } - ptrs = buf; - if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } - else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } - } - } - } else if (is_doubled && !is_doubles) { // (double*) <- (float*) - double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); - const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s); - if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } - else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } - } else if (!is_doubled && is_doubles) { // (float*) <- (double*) - float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d); - const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); - if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } - else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; } - } else { // (float*) <- (float*) - float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d); - const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s); - if (inc_d==1 && inc_s==1 && _opacity>=1) { - if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float)); - else std::memmove(ptrd,ptrs,siz*sizeof(float)); - } else { - if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) { - if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } - else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } - } else { // Overlapping buffers - CImg<floatT> buf((unsigned int)siz); - cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; } - ptrs = buf; - if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } - else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } - } - } - } - } - return _mp_arg(1); - } - - static double mp_min(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i)); - return val; - } - - static double mp_minus(_cimg_math_parser& mp) { - return -_mp_arg(2); - } - - static double mp_median(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - switch (i_end - 3) { - case 1 : return _mp_arg(3); - case 2 : return cimg::median(_mp_arg(3),_mp_arg(4)); - case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5)); - case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7)); - case 7 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9)); - case 9 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9), - _mp_arg(10),_mp_arg(11)); - case 13 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9), - _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15)); - } - CImg<doubleT> vals(i_end - 3); - double *p = vals.data(); - for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i); - return vals.median(); - } - - static double mp_modulo(_cimg_math_parser& mp) { - return cimg::mod(_mp_arg(2),_mp_arg(3)); - } - - static double mp_mul(_cimg_math_parser& mp) { - return _mp_arg(2)*_mp_arg(3); - } - - static double mp_mul2(_cimg_math_parser& mp) { - return _mp_arg(2)*_mp_arg(3)*_mp_arg(4); - } - - static double mp_neq(_cimg_math_parser& mp) { - return (double)(_mp_arg(2)!=_mp_arg(3)); - } - - static double mp_norm0(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - switch (i_end - 3) { - case 1 : return _mp_arg(3)!=0; - case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0); - } - double res = 0; - for (unsigned int i = 3; i<i_end; ++i) - res+=_mp_arg(i)==0?0:1; - return res; - } - - static double mp_norm1(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - switch (i_end - 3) { - case 1 : return cimg::abs(_mp_arg(3)); - case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4)); - } - double res = 0; - for (unsigned int i = 3; i<i_end; ++i) - res+=cimg::abs(_mp_arg(i)); - return res; - } - - static double mp_norm2(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - switch (i_end - 3) { - case 1 : return cimg::abs(_mp_arg(3)); - case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4)); - } - double res = 0; - for (unsigned int i = 3; i<i_end; ++i) - res+=cimg::sqr(_mp_arg(i)); - return std::sqrt(res); - } - - static double mp_norminf(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - switch (i_end - 3) { - case 1 : return cimg::abs(_mp_arg(3)); - case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4))); - } - double res = 0; - for (unsigned int i = 3; i<i_end; ++i) { - const double val = cimg::abs(_mp_arg(i)); - if (val>res) res = val; - } - return res; - } - - static double mp_normp(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - if (i_end==4) return cimg::abs(_mp_arg(3)); - const double p = (double)mp.opcode[3]; - double res = 0; - for (unsigned int i = 4; i<i_end; ++i) - res+=std::pow(cimg::abs(_mp_arg(i)),p); - res = std::pow(res,1/p); - return res>0?res:0.; - } - - static double mp_permutations(_cimg_math_parser& mp) { - return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4)); - } - - static double mp_polygon(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - unsigned int ind = (unsigned int)mp.opcode[3]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); - CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind]; - bool is_invalid_arguments = i_end<=4; - if (!is_invalid_arguments) { - const int nbv = (int)_mp_arg(4); - if (nbv<=0) is_invalid_arguments = true; - else { - CImg<intT> points(nbv,2,1,1,0); - CImg<T> color(img._spectrum,1,1,1,0); - float opacity = 1; - unsigned int i = 5; - cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++)); - else { is_invalid_arguments = true; break; } - if (!is_invalid_arguments) { - if (i<i_end) opacity = (float)_mp_arg(i++); - cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++); - else { color.resize(k,1,1,1,-1); break; } - color.resize(img._spectrum,1,1,1,0,2); - img.draw_polygon(points,color._data,opacity); - } - } - } - if (is_invalid_arguments) { - CImg<doubleT> args(i_end - 4); - cimg_forX(args,k) args[k] = _mp_arg(4 + k); - if (ind==~0U) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " - "Invalid arguments '%s'. ", - mp.imgin.pixel_type(),args.value_string()._data); - else - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " - "Invalid arguments '#%u%s%s'. ", - mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); - } - return cimg::type<double>::nan(); - } - - static double mp_pow(_cimg_math_parser& mp) { - const double v = _mp_arg(2), p = _mp_arg(3); - return std::pow(v,p); - } - - static double mp_pow0_25(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - return std::sqrt(std::sqrt(val)); - } - - static double mp_pow3(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - return val*val*val; - } - - static double mp_pow4(_cimg_math_parser& mp) { - const double val = _mp_arg(2); - return val*val*val*val; - } - - static double mp_print(_cimg_math_parser& mp) { - const double val = _mp_arg(1); - const bool print_char = (bool)mp.opcode[3]; - cimg_pragma_openmp(critical(mp_print)) - { - CImg<charT> expr(mp.opcode[2] - 4); - const ulongT *ptrs = mp.opcode._data + 4; - cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - cimg::strellipsize(expr); - cimg::mutex(6); - if (print_char) - std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g = '%c'",expr._data,val,(int)val); - else - std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g",expr._data,val); - std::fflush(cimg::output()); - cimg::mutex(6,0); - } - return val; - } - - static double mp_prod(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i); - return val; - } - - static double mp_copy(_cimg_math_parser& mp) { - return _mp_arg(2); - } - - static double mp_rol(_cimg_math_parser& mp) { - return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3)); - } - - static double mp_ror(_cimg_math_parser& mp) { - return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3)); - } - - static double mp_rot2d(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const float - theta = (float)_mp_arg(2)*cimg::PI/180, - ca = std::cos(theta), - sa = std::sin(theta); - *(ptrd++) = ca; - *(ptrd++) = -sa; - *(ptrd++) = sa; - *ptrd = ca; - return cimg::type<double>::nan(); - } - - static double mp_rot3d(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5); - CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta); - return cimg::type<double>::nan(); - } - - static double mp_round(_cimg_math_parser& mp) { - return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); - } - - static double mp_self_add(_cimg_math_parser& mp) { - return _mp_arg(1)+=_mp_arg(2); - } - - static double mp_self_bitwise_and(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((longT)val & (longT)_mp_arg(2)); - } - - static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((longT)val<<(unsigned int)_mp_arg(2)); - } - - static double mp_self_bitwise_or(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((longT)val | (longT)_mp_arg(2)); - } - - static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = (double)((longT)val>>(unsigned int)_mp_arg(2)); - } - - static double mp_self_decrement(_cimg_math_parser& mp) { - return --_mp_arg(1); - } - - static double mp_self_increment(_cimg_math_parser& mp) { - return ++_mp_arg(1); - } - - static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar - unsigned int - ptrd = (unsigned int)mp.opcode[1] + 1, - siz = (unsigned int)mp.opcode[2]; - mp_func op = (mp_func)mp.opcode[3]; - CImg<ulongT> l_opcode(1,3); - l_opcode[2] = mp.opcode[4]; // Scalar argument - l_opcode.swap(mp.opcode); - ulongT &target = mp.opcode[1]; - while (siz-->0) { target = ptrd++; (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type<double>::nan(); - } - - static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector - unsigned int - ptrd = (unsigned int)mp.opcode[1] + 1, - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg<ulongT> l_opcode(1,4); - l_opcode.swap(mp.opcode); - ulongT &target = mp.opcode[1], &argument = mp.opcode[2]; - while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type<double>::nan(); - } - - static double mp_self_mul(_cimg_math_parser& mp) { - return _mp_arg(1)*=_mp_arg(2); - } - - static double mp_self_div(_cimg_math_parser& mp) { - return _mp_arg(1)/=_mp_arg(2); - } - - static double mp_self_modulo(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = cimg::mod(val,_mp_arg(2)); - } - - static double mp_self_pow(_cimg_math_parser& mp) { - double &val = _mp_arg(1); - return val = std::pow(val,_mp_arg(2)); - } - - static double mp_self_sub(_cimg_math_parser& mp) { - return _mp_arg(1)-=_mp_arg(2); - } - - static double mp_set_ioff(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const longT - off = (longT)_mp_arg(2), - whds = (longT)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off<whds) img[off] = (T)val; - return val; - } - - static double mp_set_ixyzc(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const int - x = (int)_mp_arg(2), y = (int)_mp_arg(3), - z = (int)_mp_arg(4), c = (int)_mp_arg(5); - const double val = _mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && - z>=0 && z<img.depth() && c>=0 && c<img.spectrum()) - img(x,y,z,c) = (T)val; - return val; - } - - static double mp_set_joff(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), - whds = (longT)img.size(); - const double val = _mp_arg(1); - if (off>=0 && off<whds) img[off] = (T)val; - return val; - } - - static double mp_set_jxyzc(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const double - ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], - oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; - const int - x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), - z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5)); - const double val = _mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && - z>=0 && z<img.depth() && c>=0 && c<img.spectrum()) - img(x,y,z,c) = (T)val; - return val; - } - - static double mp_set_Ioff_s(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const longT - off = (longT)_mp_arg(2), - whd = (longT)img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off<whd) { - T *ptrd = &img[off]; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_set_Ioff_v(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const longT - off = (longT)_mp_arg(2), - whd = (longT)img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off<whd) { - const unsigned int vsiz = (unsigned int)mp.opcode[3]; - T *ptrd = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_set_Ixyz_s(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const int - x = (int)_mp_arg(2), - y = (int)_mp_arg(3), - z = (int)_mp_arg(4); - const T val = (T)_mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_set_Ixyz_v(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const int - x = (int)_mp_arg(2), - y = (int)_mp_arg(3), - z = (int)_mp_arg(4); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - const unsigned int vsiz = (unsigned int)mp.opcode[5]; - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_set_Joff_s(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), - whd = (longT)img.width()*img.height()*img.depth(); - const T val = (T)_mp_arg(1); - if (off>=0 && off<whd) { - T *ptrd = &img[off]; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_set_Joff_v(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const longT - off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), - whd = (longT)img.width()*img.height()*img.depth(); - const double *ptrs = &_mp_arg(1) + 1; - if (off>=0 && off<whd) { - const unsigned int vsiz = (unsigned int)mp.opcode[3]; - T *ptrd = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_set_Jxyz_s(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; - const int - x = (int)(ox + _mp_arg(2)), - y = (int)(oy + _mp_arg(3)), - z = (int)(oz + _mp_arg(4)); - const T val = (T)_mp_arg(1); - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_forC(img,c) { *ptrd = val; ptrd+=whd; } - } - return _mp_arg(1); - } - - static double mp_set_Jxyz_v(_cimg_math_parser& mp) { - CImg<T> &img = mp.imgout; - const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; - const int - x = (int)(ox + _mp_arg(2)), - y = (int)(oy + _mp_arg(3)), - z = (int)(oz + _mp_arg(4)); - const double *ptrs = &_mp_arg(1) + 1; - if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) { - const unsigned int vsiz = (unsigned int)mp.opcode[5]; - T *ptrd = &img(x,y,z); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; } - } - return cimg::type<double>::nan(); - } - - static double mp_shift(_cimg_math_parser& mp) { - double *const ptrd = &_mp_arg(1) + 1; - const double *const ptrs = &_mp_arg(2) + 1; - const unsigned int siz = (unsigned int)mp.opcode[3]; - const int - shift = (int)_mp_arg(4), - boundary_conditions = (int)_mp_arg(5); - CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions); - return cimg::type<double>::nan(); - } - - static double mp_sign(_cimg_math_parser& mp) { - return cimg::sign(_mp_arg(2)); - } - - static double mp_sin(_cimg_math_parser& mp) { - return std::sin(_mp_arg(2)); - } - - static double mp_sinc(_cimg_math_parser& mp) { - return cimg::sinc(_mp_arg(2)); - } - - static double mp_sinh(_cimg_math_parser& mp) { - return std::sinh(_mp_arg(2)); - } - - static double mp_solve(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double - *ptr1 = &_mp_arg(2) + 1, - *ptr2 = &_mp_arg(3) + 1; - const unsigned int - k = (unsigned int)mp.opcode[4], - l = (unsigned int)mp.opcode[5], - m = (unsigned int)mp.opcode[6]; - CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr2,m,l,1,1,true).get_solve(CImg<doubleT>(ptr1,k,l,1,1,true)); - return cimg::type<double>::nan(); - } - - static double mp_sort(_cimg_math_parser& mp) { - double *const ptrd = &_mp_arg(1) + 1; - const double *const ptrs = &_mp_arg(2) + 1; - const unsigned int - siz = (unsigned int)mp.opcode[3], - chunk_siz = (unsigned int)mp.opcode[5]; - const bool is_increasing = (bool)_mp_arg(4); - CImg<doubleT>(ptrd,chunk_siz,siz/chunk_siz,1,1,true) = CImg<doubleT>(ptrs,chunk_siz,siz/chunk_siz,1,1,true). - get_sort(is_increasing,chunk_siz>1?'y':0); - return cimg::type<double>::nan(); - } - - static double mp_sqr(_cimg_math_parser& mp) { - return cimg::sqr(_mp_arg(2)); - } - - static double mp_sqrt(_cimg_math_parser& mp) { - return std::sqrt(_mp_arg(2)); - } - - static double mp_srand(_cimg_math_parser& mp) { - mp.rng = (ulongT)_mp_arg(2); - return cimg::type<double>::nan(); - } - - static double mp_srand0(_cimg_math_parser& mp) { - cimg::srand(&mp.rng); -#ifdef cimg_use_openmp - mp.rng+=omp_get_thread_num(); -#endif - return cimg::type<double>::nan(); - } - - static double mp_std(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - CImg<doubleT> vals(i_end - 3); - double *p = vals.data(); - for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i); - return std::sqrt(vals.variance()); - } - - static double mp_string_init(_cimg_math_parser& mp) { - const char *ptrs = (char*)&mp.opcode[3]; - unsigned int - ptrd = (unsigned int)mp.opcode[1] + 1, - siz = (unsigned int)mp.opcode[2]; - while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++); - return cimg::type<double>::nan(); - } - - static double mp_stov(_cimg_math_parser& mp) { - const double *ptrs = &_mp_arg(2); - const ulongT siz = (ulongT)mp.opcode[3]; - longT ind = (longT)_mp_arg(4); - const bool is_strict = (bool)_mp_arg(5); - double val = cimg::type<double>::nan(); - if (ind<0 || ind>=(longT)siz) return val; - if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val; - - CImg<charT> ss(siz + 1 - ind); - char sep; - ptrs+=1 + ind; cimg_forX(ss,i) ss[i] = (char)*(ptrs++); ss.back() = 0; - - int err = cimg_sscanf(ss,"%lf%c",&val,&sep); -#if cimg_OS==2 - // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able - // to read those particular values. - if (!err && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) { - bool is_positive = true; - const char *s = ss; - if (*s=='+') ++s; else if (*s=='-') { ++s; is_positive = false; } - if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); err = 1; } - else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); err = 1; } - if (err==1 && !is_positive) val = -val; - } -#endif - if (is_strict && err!=1) return cimg::type<double>::nan(); - return val; - } - - static double mp_sub(_cimg_math_parser& mp) { - return _mp_arg(2) - _mp_arg(3); - } - - static double mp_sum(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i); - return val; - } - - static double mp_tan(_cimg_math_parser& mp) { - return std::tan(_mp_arg(2)); - } - - static double mp_tanh(_cimg_math_parser& mp) { - return std::tanh(_mp_arg(2)); - } - - static double mp_trace(_cimg_math_parser& mp) { - const double *ptrs = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode[3]; - return CImg<doubleT>(ptrs,k,k,1,1,true).trace(); - } - - static double mp_transp(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptrs = &_mp_arg(2) + 1; - const unsigned int - k = (unsigned int)mp.opcode[3], - l = (unsigned int)mp.opcode[4]; - CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose(); - return cimg::type<double>::nan(); - } - - static double mp_u(_cimg_math_parser& mp) { - return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng); - } - - static double mp_uppercase(_cimg_math_parser& mp) { - return cimg::uppercase(_mp_arg(2)); - } - - static double mp_var(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - CImg<doubleT> vals(i_end - 3); - double *p = vals.data(); - for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i); - return vals.variance(); - } - - static double mp_vector_copy(_cimg_math_parser& mp) { - std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]); - return cimg::type<double>::nan(); - } - - static double mp_vector_crop(_cimg_math_parser& mp) { - double *const ptrd = &_mp_arg(1) + 1; - const double *const ptrs = &_mp_arg(2) + 1; - const longT - length = (longT)mp.opcode[3], - start = (longT)_mp_arg(4), - sublength = (longT)mp.opcode[5]; - if (start<0 || start + sublength>length) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': " - "Out-of-bounds sub-vector request " - "(length: %ld, start: %ld, sub-length: %ld).", - mp.imgin.pixel_type(),length,start,sublength); - std::memcpy(ptrd,ptrs + start,sublength*sizeof(double)); - return cimg::type<double>::nan(); - } - - static double mp_vector_init(_cimg_math_parser& mp) { - unsigned int - ptrs = 4U, - ptrd = (unsigned int)mp.opcode[1] + 1, - siz = (unsigned int)mp.opcode[3]; - switch (mp.opcode[2] - 4) { - case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given - case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break; - default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; } - } - return cimg::type<double>::nan(); - } - - static double mp_vector_eq(_cimg_math_parser& mp) { - const double - *ptr1 = &_mp_arg(2) + 1, - *ptr2 = &_mp_arg(4) + 1; - unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n; - const int N = (int)_mp_arg(6); - const bool case_sensitive = (bool)_mp_arg(7); - bool still_equal = true; - double value; - if (!N) return true; - - // Compare all values. - if (N<0) { - if (p1>0 && p2>0) { // Vector == vector - if (p1!=p2) return false; - if (case_sensitive) - while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++); - else - while (still_equal && p1--) - still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); - return still_equal; - } else if (p1>0 && !p2) { // Vector == scalar - value = _mp_arg(4); - if (!case_sensitive) value = cimg::lowercase(value); - while (still_equal && p1--) still_equal = *(ptr1++)==value; - return still_equal; - } else if (!p1 && p2>0) { // Scalar == vector - value = _mp_arg(2); - if (!case_sensitive) value = cimg::lowercase(value); - while (still_equal && p2--) still_equal = *(ptr2++)==value; - return still_equal; - } else { // Scalar == scalar - if (case_sensitive) return _mp_arg(2)==_mp_arg(4); - else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); - } - } - - // Compare only first N values. - if (p1>0 && p2>0) { // Vector == vector - n = cimg::min((unsigned int)N,p1,p2); - if (case_sensitive) - while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++); - else - while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); - return still_equal; - } else if (p1>0 && !p2) { // Vector == scalar - n = std::min((unsigned int)N,p1); - value = _mp_arg(4); - if (!case_sensitive) value = cimg::lowercase(value); - while (still_equal && n--) still_equal = *(ptr1++)==value; - return still_equal; - } else if (!p1 && p2>0) { // Scalar == vector - n = std::min((unsigned int)N,p2); - value = _mp_arg(2); - if (!case_sensitive) value = cimg::lowercase(value); - while (still_equal && n--) still_equal = *(ptr2++)==value; - return still_equal; - } // Scalar == scalar - if (case_sensitive) return _mp_arg(2)==_mp_arg(4); - return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); - } - - static double mp_vector_off(_cimg_math_parser& mp) { - const unsigned int - ptr = (unsigned int)mp.opcode[2] + 1, - siz = (unsigned int)mp.opcode[3]; - const int off = (int)_mp_arg(4); - return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan(); - } - - static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[5] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg<ulongT> l_opcode(4); - l_opcode[2] = mp.opcode[4]; // Scalar argument1 - l_opcode.swap(mp.opcode); - ulongT &argument2 = mp.opcode[3]; - while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type<double>::nan(); - } - - static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg<ulongT> l_opcode(1,3); - l_opcode.swap(mp.opcode); - ulongT &argument = mp.opcode[2]; - while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type<double>::nan(); - } - - static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg<ulongT> l_opcode(1,4); - l_opcode[3] = mp.opcode[5]; // Scalar argument2 - l_opcode.swap(mp.opcode); - ulongT &argument1 = mp.opcode[2]; - while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type<double>::nan(); - } - - static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg<ulongT> l_opcode(1,5); - l_opcode[3] = mp.opcode[5]; // Scalar argument2 - l_opcode[4] = mp.opcode[6]; // Scalar argument3 - l_opcode.swap(mp.opcode); - ulongT &argument1 = mp.opcode[2]; - while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type<double>::nan(); - } - - static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs1 = (unsigned int)mp.opcode[4] + 1, - ptrs2 = (unsigned int)mp.opcode[5] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg<ulongT> l_opcode(1,4); - l_opcode.swap(mp.opcode); - ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3]; - while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type<double>::nan(); - } - - static double mp_vector_neq(_cimg_math_parser& mp) { - return !mp_vector_eq(mp); - } - - static double mp_vector_print(_cimg_math_parser& mp) { - const bool print_string = (bool)mp.opcode[4]; - cimg_pragma_openmp(critical(mp_vector_print)) - { - CImg<charT> expr(mp.opcode[2] - 5); - const ulongT *ptrs = mp.opcode._data + 5; - cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - cimg::strellipsize(expr); - unsigned int - ptr = (unsigned int)mp.opcode[1] + 1, - siz0 = (unsigned int)mp.opcode[3], - siz = siz0; - cimg::mutex(6); - std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",expr._data); - unsigned int count = 0; - while (siz-->0) { - if (count>=64 && siz>=64) { - std::fprintf(cimg::output(),"...,"); - ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64; - siz = 64; - } else std::fprintf(cimg::output(),"%g%s",mp.mem[ptr++],siz?",":""); - ++count; - } - if (print_string) { - CImg<charT> str(siz0 + 1); - ptr = (unsigned int)mp.opcode[1] + 1; - for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++]; - str[siz0] = 0; - cimg::strellipsize(str,1024,false); - std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0); - } else std::fprintf(cimg::output()," ] (size: %u)",siz0); - std::fflush(cimg::output()); - cimg::mutex(6,0); - } - return cimg::type<double>::nan(); - } - - static double mp_vector_resize(_cimg_math_parser& mp) { - double *const ptrd = &_mp_arg(1) + 1; - const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4]; - const int - interpolation = (int)_mp_arg(5), - boundary_conditions = (int)_mp_arg(6); - if (p2) { // Resize vector - const double *const ptrs = &_mp_arg(3) + 1; - CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true). - get_resize(p1,1,1,1,interpolation,boundary_conditions); - } else { // Resize scalar - const double value = _mp_arg(3); - CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation, - boundary_conditions); - } - return cimg::type<double>::nan(); - } - - static double mp_vector_reverse(_cimg_math_parser& mp) { - double *const ptrd = &_mp_arg(1) + 1; - const double *const ptrs = &_mp_arg(2) + 1; - const unsigned int p1 = (unsigned int)mp.opcode[3]; - CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x'); - return cimg::type<double>::nan(); - } - - static double mp_vector_set_off(_cimg_math_parser& mp) { - const unsigned int - ptr = (unsigned int)mp.opcode[2] + 1, - siz = (unsigned int)mp.opcode[3]; - const int off = (int)_mp_arg(4); - if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(5); - return _mp_arg(5); - } - - static double mp_vtos(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - sizd = (unsigned int)mp.opcode[2], - sizs = (unsigned int)mp.opcode[4]; - const int nb_digits = (int)_mp_arg(5); - CImg<charT> format(8); - switch (nb_digits) { - case -1 : std::strcpy(format,"%g"); break; - case 0 : std::strcpy(format,"%.17g"); break; - default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits); - } - CImg<charT> str; - if (sizs) { // Vector expression - const double *ptrs = &_mp_arg(3) + 1; - CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str); - } else { // Scalar expression - str.assign(sizd + 1); - cimg_snprintf(str,sizd + 1,format,_mp_arg(3)); - } - const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1); - CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1); - return cimg::type<double>::nan(); - } - - static double mp_while(_cimg_math_parser& mp) { - const ulongT - mem_body = mp.opcode[1], - mem_cond = mp.opcode[2]; - const CImg<ulongT> - *const p_cond = ++mp.p_code, - *const p_body = p_cond + mp.opcode[3], - *const p_end = p_body + mp.opcode[4]; - const unsigned int vsiz = (unsigned int)mp.opcode[5]; - bool is_cond = false; - if (mp.opcode[6]) { // Set default value for result and condition if necessary - if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan()); - else mp.mem[mem_body] = cimg::type<double>::nan(); - } - if (mp.opcode[7]) mp.mem[mem_cond] = 0; - const unsigned int _break_type = mp.break_type; - mp.break_type = 0; - do { - for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.break_type==1) break; - is_cond = (bool)mp.mem[mem_cond]; - if (is_cond && !mp.break_type) // Evaluate body - for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) { - mp.opcode._data = mp.p_code->_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; - } while (is_cond); - - mp.break_type = _break_type; - mp.p_code = p_end - 1; - return mp.mem[mem_body]; - } - - static double mp_Ioff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3), - vsiz = (unsigned int)mp.opcode[4]; - const CImg<T> &img = mp.imgin; - const longT - off = (longT)_mp_arg(2), - whd = (longT)img.width()*img.height()*img.depth(); - const T *ptrs; - if (off>=0 && off<whd) { - ptrs = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); - ptrs = &img[moff<whd?moff:whd2 - moff - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - case 2 : // Periodic - ptrs = &img[cimg::mod(off,whd)]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - case 1 : // Neumann - ptrs = off<0?&img[0]:&img[whd - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - default : // Dirichlet - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - - static double mp_Ixyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - interpolation = (unsigned int)_mp_arg(5), - boundary_conditions = (unsigned int)_mp_arg(6), - vsiz = (unsigned int)mp.opcode[7]; - const CImg<T> &img = mp.imgin; - const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - const T *ptrs; - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 2 : { // Periodic - const int - cx = cimg::mod((int)x,img.width()), - cy = cimg::mod((int)y,img.height()), - cz = cimg::mod((int)z,img.depth()); - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 1 : { // Neumann - ptrs = &img._atXYZ((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - default : // Dirichlet - if (img.containsXYZC((int)x,(int)y,(int)z)) { - ptrs = &img((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,vsiz*sizeof(double)); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 2 : { // Periodic - const float - cx = cimg::mod((float)x,(float)img.width()), - cy = cimg::mod((float)y,(float)img.height()), - cz = cimg::mod((float)z,(float)img.depth()); - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 1 : // Neumann - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c); - break; - default : // Dirichlet - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0); - } - return cimg::type<double>::nan(); - } - - static double mp_Joff(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - boundary_conditions = (unsigned int)_mp_arg(3), - vsiz = (unsigned int)mp.opcode[4]; - const CImg<T> &img = mp.imgin; - const int - ox = (int)mp.mem[_cimg_mp_slot_x], - oy = (int)mp.mem[_cimg_mp_slot_y], - oz = (int)mp.mem[_cimg_mp_slot_z]; - const longT - off = img.offset(ox,oy,oz) + (longT)_mp_arg(2), - whd = (longT)img.width()*img.height()*img.depth(); - const T *ptrs; - if (off>=0 && off<whd) { - ptrs = &img[off]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - if (img._data) switch (boundary_conditions) { - case 3 : { // Mirror - const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); - ptrs = &img[moff<whd?moff:whd2 - moff - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - } - case 2 : // Periodic - ptrs = &img[cimg::mod(off,whd)]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - case 1 : // Neumann - ptrs = off<0?&img[0]:&img[whd - 1]; - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return cimg::type<double>::nan(); - default : // Dirichlet - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - std::memset(ptrd,0,vsiz*sizeof(double)); - return cimg::type<double>::nan(); - } - - static double mp_Jxyz(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const unsigned int - interpolation = (unsigned int)_mp_arg(5), - boundary_conditions = (unsigned int)_mp_arg(6), - vsiz = (unsigned int)mp.opcode[7]; - const CImg<T> &img = mp.imgin; - const double - ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], - x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4); - const ulongT whd = (ulongT)img._width*img._height*img._depth; - const T *ptrs; - if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation - case 3 : { // Mirror - const int - w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), - mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 2 : { // Periodic - const int - cx = cimg::mod((int)x,img.width()), - cy = cimg::mod((int)y,img.height()), - cz = cimg::mod((int)z,img.depth()); - ptrs = &img(cx,cy,cz); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - case 1 : { // Neumann - ptrs = &img._atXYZ((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } break; - default : // Dirichlet - if (img.containsXYZC((int)x,(int)y,(int)z)) { - ptrs = &img((int)x,(int)y,(int)z); - cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } - } else std::memset(ptrd,0,vsiz*sizeof(double)); - } else switch (boundary_conditions) { // Linear interpolation - case 3 : { // Mirror - const float - w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), - mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), - cx = mx<img.width()?mx:w2 - mx - 1, - cy = my<img.height()?my:h2 - my - 1, - cz = mz<img.depth()?mz:d2 - mz - 1; - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 2 : { // Periodic - const float - cx = cimg::mod((float)x,(float)img.width()), - cy = cimg::mod((float)y,(float)img.height()), - cz = cimg::mod((float)z,(float)img.depth()); - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c); - } break; - case 1 : // Neumann - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c); - break; - default : // Dirichlet - cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0); - } - return cimg::type<double>::nan(); - } - -#undef _mp_arg - - }; // struct _cimg_math_parser {} - -#define _cimg_create_pointwise_functions(name,func,min_size) \ - CImg<T>& name() { \ - if (is_empty()) return *this; \ - cimg_openmp_for(*this,func((double)*ptr),min_size); \ - return *this; \ - } \ - CImg<Tfloat> get_##name() const { \ - return CImg<Tfloat>(*this,false).name(); \ - } - - //! Compute the square value of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square value \f$I_{(x,y,z,c)}^2\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - \par Example - \code - const CImg<float> img("reference.jpg"); - (img,img.get_sqr().normalize(0,255)).display(); - \endcode - \image html ref_sqr.jpg - **/ - _cimg_create_pointwise_functions(sqr,cimg::sqr,524288) - - //! Compute the square root of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square root \f$\sqrt{I_{(x,y,z,c)}}\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - \par Example - \code - const CImg<float> img("reference.jpg"); - (img,img.get_sqrt().normalize(0,255)).display(); - \endcode - \image html ref_sqrt.jpg - **/ - _cimg_create_pointwise_functions(sqrt,std::sqrt,8192) - - //! Compute the exponential of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its exponential \f$e^{I_{(x,y,z,c)}}\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(exp,std::exp,4096) - - //! Compute the logarithm of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm - \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(log,std::log,262144) - - //! Compute the base-2 logarithm of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm - \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(log2,cimg::log2,4096) - - //! Compute the base-10 logarithm of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm - \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(log10,std::log10,4096) - - //! Compute the absolute value of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its absolute value \f$|I_{(x,y,z,c)}|\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(abs,cimg::abs,524288) - - //! Compute the sign of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign - \f$\mathrm{sign}(I_{(x,y,z,c)})\f$. - \note - - The sign is set to: - - \c 1 if pixel value is strictly positive. - - \c -1 if pixel value is strictly negative. - - \c 0 if pixel value is equal to \c 0. - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(sign,cimg::sign,32768) - - //! Compute the cosine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its cosine \f$\cos(I_{(x,y,z,c)})\f$. - \note - - Pixel values are regarded as being in \e radian. - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(cos,std::cos,8192) - - //! Compute the sine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sine \f$\sin(I_{(x,y,z,c)})\f$. - \note - - Pixel values are regarded as being in \e radian. - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(sin,std::sin,8192) - - //! Compute the sinc of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc - \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$. - \note - - Pixel values are regarded as being exin \e radian. - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(sinc,cimg::sinc,2048) - - //! Compute the tangent of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its tangent \f$\tan(I_{(x,y,z,c)})\f$. - \note - - Pixel values are regarded as being exin \e radian. - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(tan,std::tan,2048) - - //! Compute the hyperbolic cosine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine - \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(cosh,std::cosh,2048) - - //! Compute the hyperbolic sine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine - \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(sinh,std::sinh,2048) - - //! Compute the hyperbolic tangent of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent - \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(tanh,std::tanh,2048) - - //! Compute the arccosine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine - \f$\mathrm{acos}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(acos,std::acos,8192) - - //! Compute the arcsine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine - \f$\mathrm{asin}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(asin,std::asin,8192) - - //! Compute the arctangent of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent - \f$\mathrm{atan}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(atan,std::atan,8192) - - //! Compute the arctangent2 of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2 - \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$. - \param img Image whose pixel values specify the second argument of the \c atan2() function. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - \par Example - \code - const CImg<float> - img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2' - img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2' - img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value - (img_x,img_y,img_atan2).display(); - \endcode - **/ - template<typename t> - CImg<T>& atan2(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return atan2(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++)); - } - return *this; - } - - //! Compute the arctangent2 of each pixel value \newinstance. - template<typename t> - CImg<Tfloat> get_atan2(const CImg<t>& img) const { - return CImg<Tfloat>(*this,false).atan2(img); - } - - //! Compute the hyperbolic arccosine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh - \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(acosh,cimg::acosh,8192) - - //! Compute the hyperbolic arcsine of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine - \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(asinh,cimg::asinh,8192) - - //! Compute the hyperbolic arctangent of each pixel value. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent - \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - **/ - _cimg_create_pointwise_functions(atanh,cimg::atanh,8192) - - //! In-place pointwise multiplication. - /** - Compute the pointwise multiplication between the image instance and the specified input image \c img. - \param img Input image, as the second operand of the multiplication. - \note - - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication - instead of an addition. - - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead. - \par Example - \code - CImg<float> - img("reference.jpg"), - shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false); - shade.normalize(0,1); - (img,shade,img.get_mul(shade)).display(); - \endcode - **/ - template<typename t> - CImg<T>& mul(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return mul(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)(*ptrd * *(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++)); - } - return *this; - } - - //! In-place pointwise multiplication \newinstance. - template<typename t> - CImg<_cimg_Tt> get_mul(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this,false).mul(img); - } - - //! In-place pointwise division. - /** - Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication. - **/ - template<typename t> - CImg<T>& div(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return div(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)(*ptrd / *(ptrs++)); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++)); - } - return *this; - } - - //! In-place pointwise division \newinstance. - template<typename t> - CImg<_cimg_Tt> get_div(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this,false).div(img); - } - - //! Raise each pixel value to a specified power. - /** - Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its power \f$I_{(x,y,z,c)}^p\f$. - \param p Exponent value. - \note - - The \inplace of this method statically casts the computed values to the pixel type \c T. - - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued. - \par Example - \code - const CImg<float> - img0("reference.jpg"), // Load reference color image - img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8 - img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5 - (img0,img1,img2).display(); - \endcode - **/ - CImg<T>& pow(const double p) { - if (is_empty()) return *this; - if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; } - if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; } - if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; } - if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; } - if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; } - if (p==0) return fill((T)1); - if (p==0.5) return sqrt(); - if (p==1) return *this; - if (p==2) return sqr(); - if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; } - if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; } - cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024); - return *this; - } - - //! Raise each pixel value to a specified power \newinstance. - CImg<Tfloat> get_pow(const double p) const { - return CImg<Tfloat>(*this,false).pow(p); - } - - //! Raise each pixel value to a power, specified from an expression. - /** - Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. - **/ - CImg<T>& pow(const char *const expression) { - return pow((+*this)._fill(expression,true,1,0,0,"pow",this)); - } - - //! Raise each pixel value to a power, specified from an expression \newinstance. - CImg<Tfloat> get_pow(const char *const expression) const { - return CImg<Tfloat>(*this,false).pow(expression); - } - - //! Raise each pixel value to a power, pointwisely specified from another image. - /** - Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition. - **/ - template<typename t> - CImg<T>& pow(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return pow(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++))); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++))); - } - return *this; - } - - //! Raise each pixel value to a power, pointwisely specified from another image \newinstance. - template<typename t> - CImg<Tfloat> get_pow(const CImg<t>& img) const { - return CImg<Tfloat>(*this,false).pow(img); - } - - //! Compute the bitwise left rotation of each pixel value. - /** - Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift. - **/ - CImg<T>& rol(const unsigned int n=1) { - if (is_empty()) return *this; - cimg_openmp_for(*this,cimg::rol(*ptr,n),32768); - return *this; - } - - //! Compute the bitwise left rotation of each pixel value \newinstance. - CImg<T> get_rol(const unsigned int n=1) const { - return (+*this).rol(n); - } - - //! Compute the bitwise left rotation of each pixel value. - /** - Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. - **/ - CImg<T>& rol(const char *const expression) { - return rol((+*this)._fill(expression,true,1,0,0,"rol",this)); - } - - //! Compute the bitwise left rotation of each pixel value \newinstance. - CImg<T> get_rol(const char *const expression) const { - return (+*this).rol(expression); - } - - //! Compute the bitwise left rotation of each pixel value. - /** - Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift. - **/ - template<typename t> - CImg<T>& rol(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return rol(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++))); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++))); - } - return *this; - } - - //! Compute the bitwise left rotation of each pixel value \newinstance. - template<typename t> - CImg<T> get_rol(const CImg<t>& img) const { - return (+*this).rol(img); - } - - //! Compute the bitwise right rotation of each pixel value. - /** - Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift. - **/ - CImg<T>& ror(const unsigned int n=1) { - if (is_empty()) return *this; - cimg_openmp_for(*this,cimg::ror(*ptr,n),32768); - return *this; - } - - //! Compute the bitwise right rotation of each pixel value \newinstance. - CImg<T> get_ror(const unsigned int n=1) const { - return (+*this).ror(n); - } - - //! Compute the bitwise right rotation of each pixel value. - /** - Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. - **/ - CImg<T>& ror(const char *const expression) { - return ror((+*this)._fill(expression,true,1,0,0,"ror",this)); - } - - //! Compute the bitwise right rotation of each pixel value \newinstance. - CImg<T> get_ror(const char *const expression) const { - return (+*this).ror(expression); - } - - //! Compute the bitwise right rotation of each pixel value. - /** - Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift. - **/ - template<typename t> - CImg<T>& ror(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return ror(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++))); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++))); - } - return *this; - } - - //! Compute the bitwise right rotation of each pixel value \newinstance. - template<typename t> - CImg<T> get_ror(const CImg<t>& img) const { - return (+*this).ror(img); - } - - //! Pointwise min operator between instance image and a value. - /** - \param val Value used as the reference argument of the min operator. - \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by - \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$. - **/ - CImg<T>& min(const T& value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,std::min(*ptr,value),65536); - return *this; - } - - //! Pointwise min operator between instance image and a value \newinstance. - CImg<T> get_min(const T& value) const { - return (+*this).min(value); - } - - //! Pointwise min operator between two images. - /** - \param img Image used as the reference argument of the min operator. - \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by - \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. - **/ - template<typename t> - CImg<T>& min(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return min(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = std::min((T)*(ptrs++),*ptrd); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd); - } - return *this; - } - - //! Pointwise min operator between two images \newinstance. - template<typename t> - CImg<_cimg_Tt> get_min(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this,false).min(img); - } - - //! Pointwise min operator between an image and an expression. - /** - \param expression Math formula as a C-string. - \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by - \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. - **/ - CImg<T>& min(const char *const expression) { - return min((+*this)._fill(expression,true,1,0,0,"min",this)); - } - - //! Pointwise min operator between an image and an expression \newinstance. - CImg<Tfloat> get_min(const char *const expression) const { - return CImg<Tfloat>(*this,false).min(expression); - } - - //! Pointwise max operator between instance image and a value. - /** - \param val Value used as the reference argument of the max operator. - \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by - \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$. - **/ - CImg<T>& max(const T& value) { - if (is_empty()) return *this; - cimg_openmp_for(*this,std::max(*ptr,value),65536); - return *this; - } - - //! Pointwise max operator between instance image and a value \newinstance. - CImg<T> get_max(const T& value) const { - return (+*this).max(value); - } - - //! Pointwise max operator between two images. - /** - \param img Image used as the reference argument of the max operator. - \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by - \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. - **/ - template<typename t> - CImg<T>& max(const CImg<t>& img) { - const ulongT siz = size(), isiz = img.size(); - if (siz && isiz) { - if (is_overlapped(img)) return max(+img); - T *ptrd = _data, *const ptre = _data + siz; - if (siz>isiz) for (ulongT n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) - *ptrd = std::max((T)*(ptrs++),*ptrd); - for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd); - } - return *this; - } - - //! Pointwise max operator between two images \newinstance. - template<typename t> - CImg<_cimg_Tt> get_max(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this,false).max(img); - } - - //! Pointwise max operator between an image and an expression. - /** - \param expression Math formula as a C-string. - \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by - \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. - **/ - CImg<T>& max(const char *const expression) { - return max((+*this)._fill(expression,true,1,0,0,"max",this)); - } - - //! Pointwise max operator between an image and an expression \newinstance. - CImg<Tfloat> get_max(const char *const expression) const { - return CImg<Tfloat>(*this,false).max(expression); - } - - //! Return a reference to the minimum pixel value. - /** - **/ - T& min() { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "min(): Empty instance.", - cimg_instance); - T *ptr_min = _data; - T min_value = *ptr_min; - cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs); - return *ptr_min; - } - - //! Return a reference to the minimum pixel value \const. - const T& min() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "min(): Empty instance.", - cimg_instance); - const T *ptr_min = _data; - T min_value = *ptr_min; - cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs); - return *ptr_min; - } - - //! Return a reference to the maximum pixel value. - /** - **/ - T& max() { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "max(): Empty instance.", - cimg_instance); - T *ptr_max = _data; - T max_value = *ptr_max; - cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); - return *ptr_max; - } - - //! Return a reference to the maximum pixel value \const. - const T& max() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "max(): Empty instance.", - cimg_instance); - const T *ptr_max = _data; - T max_value = *ptr_max; - cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); - return *ptr_max; - } - - //! Return a reference to the minimum pixel value as well as the maximum pixel value. - /** - \param[out] max_val Maximum pixel value. - **/ - template<typename t> - T& min_max(t& max_val) { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "min_max(): Empty instance.", - cimg_instance); - T *ptr_min = _data; - T min_value = *ptr_min, max_value = min_value; - cimg_for(*this,ptrs,T) { - const T val = *ptrs; - if (val<min_value) { min_value = val; ptr_min = ptrs; } - if (val>max_value) max_value = val; - } - max_val = (t)max_value; - return *ptr_min; - } - - //! Return a reference to the minimum pixel value as well as the maximum pixel value \const. - template<typename t> - const T& min_max(t& max_val) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "min_max(): Empty instance.", - cimg_instance); - const T *ptr_min = _data; - T min_value = *ptr_min, max_value = min_value; - cimg_for(*this,ptrs,T) { - const T val = *ptrs; - if (val<min_value) { min_value = val; ptr_min = ptrs; } - if (val>max_value) max_value = val; - } - max_val = (t)max_value; - return *ptr_min; - } - - //! Return a reference to the maximum pixel value as well as the minimum pixel value. - /** - \param[out] min_val Minimum pixel value. - **/ - template<typename t> - T& max_min(t& min_val) { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "max_min(): Empty instance.", - cimg_instance); - T *ptr_max = _data; - T max_value = *ptr_max, min_value = max_value; - cimg_for(*this,ptrs,T) { - const T val = *ptrs; - if (val>max_value) { max_value = val; ptr_max = ptrs; } - if (val<min_value) min_value = val; - } - min_val = (t)min_value; - return *ptr_max; - } - - //! Return a reference to the maximum pixel value as well as the minimum pixel value \const. - template<typename t> - const T& max_min(t& min_val) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "max_min(): Empty instance.", - cimg_instance); - const T *ptr_max = _data; - T max_value = *ptr_max, min_value = max_value; - cimg_for(*this,ptrs,T) { - const T val = *ptrs; - if (val>max_value) { max_value = val; ptr_max = ptrs; } - if (val<min_value) min_value = val; - } - min_val = (t)min_value; - return *ptr_max; - } - - //! Return the kth smallest pixel value. - /** - \param k Rank of the search smallest element. - **/ - T kth_smallest(const ulongT k) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "kth_smallest(): Empty instance.", - cimg_instance); - CImg<T> arr(*this,false); - ulongT l = 0, ir = size() - 1; - for ( ; ; ) { - if (ir<=l + 1) { - if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]); - return arr[k]; - } else { - const ulongT mid = (l + ir)>>1; - cimg::swap(arr[mid],arr[l + 1]); - if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); - if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]); - if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]); - ulongT i = l + 1, j = ir; - const T pivot = arr[l + 1]; - for ( ; ; ) { - do ++i; while (arr[i]<pivot); - do --j; while (arr[j]>pivot); - if (j<i) break; - cimg::swap(arr[i],arr[j]); - } - arr[l + 1] = arr[j]; - arr[j] = pivot; - if (j>=k) ir = j - 1; - if (j<=k) l = i; - } - } - } - - //! Return the median pixel value. - /** - **/ - T median() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "median(): Empty instance.", - cimg_instance); - const ulongT s = size(); - switch (s) { - case 1 : return _data[0]; - case 2 : return cimg::median(_data[0],_data[1]); - case 3 : return cimg::median(_data[0],_data[1],_data[2]); - case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]); - case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]); - case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]); - case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8], - _data[9],_data[10],_data[11],_data[12]); - } - const T res = kth_smallest(s>>1); - return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2); - } - - //! Return the product of all the pixel values. - /** - **/ - double product() const { - if (is_empty()) return 0; - double res = 1; - cimg_for(*this,ptrs,T) res*=(double)*ptrs; - return res; - } - - //! Return the sum of all the pixel values. - /** - **/ - double sum() const { - double res = 0; - cimg_for(*this,ptrs,T) res+=(double)*ptrs; - return res; - } - - //! Return the average pixel value. - /** - **/ - double mean() const { - double res = 0; - cimg_for(*this,ptrs,T) res+=(double)*ptrs; - return res/size(); - } - - //! Return the variance of the pixel values. - /** - \param variance_method Method used to estimate the variance. Can be: - - \c 0: Second moment, computed as - \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 = - 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$ - with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$. - - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$. - - \c 2: Least median of squares. - - \c 3: Least trimmed of squares. - **/ - double variance(const unsigned int variance_method=1) const { - double foo; - return variance_mean(variance_method,foo); - } - - //! Return the variance as well as the average of the pixel values. - /** - \param variance_method Method used to estimate the variance (see variance(const unsigned int) const). - \param[out] mean Average pixel value. - **/ - template<typename t> - double variance_mean(const unsigned int variance_method, t& mean) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "variance_mean(): Empty instance.", - cimg_instance); - - double variance = 0, average = 0; - const ulongT siz = size(); - switch (variance_method) { - case 0 : { // Least mean square (standard definition) - double S = 0, S2 = 0; - cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } - variance = (S2 - S*S/siz)/siz; - average = S; - } break; - case 1 : { // Least mean square (robust definition) - double S = 0, S2 = 0; - cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } - variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; - average = S; - } break; - case 2 : { // Least Median of Squares (MAD) - CImg<Tfloat> buf(*this,false); - buf.sort(); - const ulongT siz2 = siz>>1; - const double med_i = (double)buf[siz2]; - cimg_for(buf,ptrs,Tfloat) { - const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; - } - buf.sort(); - const double sig = (double)(1.4828*buf[siz2]); - variance = sig*sig; - } break; - default : { // Least trimmed of Squares - CImg<Tfloat> buf(*this,false); - const ulongT siz2 = siz>>1; - cimg_for(buf,ptrs,Tfloat) { - const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; - } - buf.sort(); - double a = 0; - const Tfloat *ptrs = buf._data; - for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++); - const double sig = (double)(2.6477*std::sqrt(a/siz2)); - variance = sig*sig; - } - } - mean = (t)(average/siz); - return variance>0?variance:0; - } - - //! Return estimated variance of the noise. - /** - \param variance_method Method used to compute the variance (see variance(const unsigned int) const). - \note Because of structures such as edges in images it is - recommanded to use a robust variance estimation. The variance of the - noise is estimated by computing the variance of the Laplacian \f$(\Delta - I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]= - \sigma^2\f$ where \f$\sigma\f$ is the noise variance. - **/ - double variance_noise(const unsigned int variance_method=2) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "variance_noise(): Empty instance.", - cimg_instance); - - const ulongT siz = size(); - if (!siz || !_data) return 0; - if (variance_method>1) { // Compute a scaled version of the Laplacian - CImg<Tdouble> tmp(*this,false); - if (_depth==1) { - const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 && - _spectrum>=2)) - cimg_forC(*this,c) { - CImg_3x3(I,T); - cimg_for3x3(*this,x,y,0,c,I,T) { - tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn + - (double)Icp - 4*(double)Icc); - } - } - } else { - const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 && - _spectrum>=2)) - cimg_forC(*this,c) { - CImg_3x3x3(I,T); - cimg_for3x3x3(*this,x,y,z,c,I,T) { - tmp(x,y,z,c) = cste*( - (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc + - (double)Iccn + (double)Iccp - 6*(double)Iccc); - } - } - } - return tmp.variance(variance_method); - } - - // Version that doesn't need intermediate images. - double variance = 0, S = 0, S2 = 0; - if (_depth==1) { - const double cste = 1./std::sqrt(20.); - CImg_3x3(I,T); - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { - const double val = cste*((double)Inc + (double)Ipc + - (double)Icn + (double)Icp - 4*(double)Icc); - S+=val; S2+=val*val; - } - } else { - const double cste = 1./std::sqrt(42.); - CImg_3x3x3(I,T); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { - const double val = cste * - ((double)Incc + (double)Ipcc + (double)Icnc + - (double)Icpc + - (double)Iccn + (double)Iccp - 6*(double)Iccc); - S+=val; S2+=val*val; - } - } - if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; - else variance = (S2 - S*S/siz)/siz; - return variance>0?variance:0; - } - - //! Compute the MSE (Mean-Squared Error) between two images. - /** - \param img Image used as the second argument of the MSE operator. - **/ - template<typename t> - double MSE(const CImg<t>& img) const { - if (img.size()!=size()) - throw CImgArgumentException(_cimg_instance - "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.", - cimg_instance, - img._width,img._height,img._depth,img._spectrum,img._data); - double vMSE = 0; - const t* ptr2 = img._data; - cimg_for(*this,ptr1,T) { - const double diff = (double)*ptr1 - (double)*(ptr2++); - vMSE+=diff*diff; - } - const ulongT siz = img.size(); - if (siz) vMSE/=siz; - return vMSE; - } - - //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images. - /** - \param img Image used as the second argument of the PSNR operator. - \param max_value Maximum theoretical value of the signal. - **/ - template<typename t> - double PSNR(const CImg<t>& img, const double max_value=255) const { - const double vMSE = (double)std::sqrt(MSE(img)); - return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max()); - } - - //! Evaluate math formula. - /** - \param expression Math formula, as a C-string. - \param x Value of the pre-defined variable \c x. - \param y Value of the pre-defined variable \c y. - \param z Value of the pre-defined variable \c z. - \param c Value of the pre-defined variable \c c. - \param list_inputs A list of input images attached to the specified math formula. - \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. - **/ - double eval(const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) { - return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); - } - - //! Evaluate math formula \const. - double eval(const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const { - return _eval(0,expression,x,y,z,c,list_inputs,list_outputs); - } - - double _eval(CImg<T> *const img_output, const char *const expression, - const double x, const double y, const double z, const double c, - const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const { - if (!expression || !*expression) return 0; - if (!expression[1]) switch (*expression) { // Single-char optimization - case 'w' : return (double)_width; - case 'h' : return (double)_height; - case 'd' : return (double)_depth; - case 's' : return (double)_spectrum; - case 'r' : return (double)_is_shared; - } - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || - *expression=='*' || *expression==':'),"eval", - *this,img_output,list_inputs,list_outputs,false); - const double val = mp(x,y,z,c); - mp.end(); - return val; - } - - //! Evaluate math formula. - /** - \param[out] output Contains values of output vector returned by the evaluated expression - (or is empty if the returned type is scalar). - \param expression Math formula, as a C-string. - \param x Value of the pre-defined variable \c x. - \param y Value of the pre-defined variable \c y. - \param z Value of the pre-defined variable \c z. - \param c Value of the pre-defined variable \c c. - \param list_inputs A list of input images attached to the specified math formula. - \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. - **/ - template<typename t> - void eval(CImg<t> &output, const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) { - _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); - } - - //! Evaluate math formula \const. - template<typename t> - void eval(CImg<t>& output, const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const { - _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); - } - - template<typename t> - void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression, - const double x, const double y, const double z, const double c, - const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const { - if (!expression || !*expression) { output.assign(1); *output = 0; } - if (!expression[1]) switch (*expression) { // Single-char optimization - case 'w' : output.assign(1); *output = (t)_width; break; - case 'h' : output.assign(1); *output = (t)_height; break; - case 'd' : output.assign(1); *output = (t)_depth; break; - case 's' : output.assign(1); *output = (t)_spectrum; break; - case 'r' : output.assign(1); *output = (t)_is_shared; break; - } - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || - *expression=='*' || *expression==':'),"eval", - *this,img_output,list_inputs,list_outputs,false); - output.assign(1,std::max(1U,mp.result_dim)); - mp(x,y,z,c,output._data); - mp.end(); - } - - //! Evaluate math formula on a set of variables. - /** - \param expression Math formula, as a C-string. - \param xyzc Set of values (x,y,z,c) used for the evaluation. - \param list_inputs A list of input images attached to the specified math formula. - \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. - **/ - template<typename t> - CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) { - return _eval(this,expression,xyzc,list_inputs,list_outputs); - } - - //! Evaluate math formula on a set of variables \const. - template<typename t> - CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const { - return _eval(0,expression,xyzc,list_inputs,list_outputs); - } - - template<typename t> - CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const { - CImg<doubleT> res(1,xyzc.size()/4); - if (!expression || !*expression) return res.fill(0); - _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false); -#ifdef cimg_use_openmp - cimg_pragma_openmp(parallel if (res._height>=512)) - { - _cimg_math_parser - _mp = omp_get_thread_num()?mp:_cimg_math_parser(), - &lmp = omp_get_thread_num()?_mp:mp; - cimg_pragma_openmp(for) - for (unsigned int i = 0; i<res._height; ++i) { - const unsigned int i4 = 4*i; - const double - x = (double)xyzc[i4], y = (double)xyzc[i4 + 1], - z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3]; - res[i] = lmp(x,y,z,c); - } - } -#else - const t *ps = xyzc._data; - cimg_for(res,pd,double) { - const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++); - *pd = mp(x,y,z,c); - } -#endif - mp.end(); - return res; - } - - //! Compute statistics vector from the pixel values. - /* - \param variance_method Method used to compute the variance (see variance(const unsigned int) const). - \return Statistics vector as - <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>. - **/ - CImg<Tdouble> get_stats(const unsigned int variance_method=1) const { - if (is_empty()) return CImg<doubleT>(); - const ulongT siz = size(); - const longT off_end = (longT)siz; - double S = 0, S2 = 0, P = 1; - longT offm = 0, offM = 0; - T m = *_data, M = m; - - cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) { - longT loffm = 0, loffM = 0; - T lm = *_data, lM = lm; - cimg_pragma_openmp(for) - for (longT off = 0; off<off_end; ++off) { - const T val = _data[off]; - const double _val = (double)val; - if (val<lm) { lm = val; loffm = off; } - if (val>lM) { lM = val; loffM = off; } - S+=_val; - S2+=_val*_val; - P*=_val; - } - cimg_pragma_openmp(critical(get_stats)) { - if (lm<m || (lm==m && loffm<offm)) { m = lm; offm = loffm; } - if (lM>M || (lM==M && loffM<offM)) { M = lM; offM = loffM; } - } - } - - const double - mean_value = S/siz, - _variance_value = variance_method==0?(S2 - S*S/siz)/siz: - (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0): - variance(variance_method)), - variance_value = _variance_value>0?_variance_value:0; - int - xm = 0, ym = 0, zm = 0, cm = 0, - xM = 0, yM = 0, zM = 0, cM = 0; - contains(_data[offm],xm,ym,zm,cm); - contains(_data[offM],xM,yM,zM,cM); - return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value, - (double)xm,(double)ym,(double)zm,(double)cm, - (double)xM,(double)yM,(double)zM,(double)cM, - S,P); - } - - //! Compute statistics vector from the pixel values \inplace. - CImg<T>& stats(const unsigned int variance_method=1) { - return get_stats(variance_method).move_to(*this); - } - - //@} - //------------------------------------- - // - //! \name Vector / Matrix Operations - //@{ - //------------------------------------- - - //! Compute norm of the image, viewed as a matrix. - /** - \param magnitude_type Norm type. Can be: - - \c -1: Linf-norm - - \c 0: L0-norm - - \c 1: L1-norm - - \c 2: L2-norm - **/ - double magnitude(const int magnitude_type=2) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "magnitude(): Empty instance.", - cimg_instance); - double res = 0; - switch (magnitude_type) { - case -1 : { - cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } - } break; - case 1 : { - cimg_for(*this,ptrs,T) res+=(double)cimg::abs(*ptrs); - } break; - default : { - cimg_for(*this,ptrs,T) res+=(double)cimg::sqr(*ptrs); - res = (double)std::sqrt(res); - } - } - return res; - } - - //! Compute the trace of the image, viewed as a matrix. - /** - **/ - double trace() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "trace(): Empty instance.", - cimg_instance); - double res = 0; - cimg_forX(*this,k) res+=(double)(*this)(k,k); - return res; - } - - //! Compute the determinant of the image, viewed as a matrix. - /** - **/ - double det() const { - if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1) - throw CImgInstanceException(_cimg_instance - "det(): Instance is not a square matrix.", - cimg_instance); - - switch (_width) { - case 1 : return (double)((*this)(0,0)); - case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0)); - case 3 : { - const double - a = (double)_data[0], d = (double)_data[1], g = (double)_data[2], - b = (double)_data[3], e = (double)_data[4], h = (double)_data[5], - c = (double)_data[6], f = (double)_data[7], i = (double)_data[8]; - return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e; - } - default : { - CImg<Tfloat> lu(*this,false); - CImg<uintT> indx; - bool d; - lu._LU(indx,d); - double res = d?(double)1:(double)-1; - cimg_forX(lu,i) res*=lu(i,i); - return res; - } - } - } - - //! Compute the dot product between instance and argument, viewed as matrices. - /** - \param img Image used as a second argument of the dot product. - **/ - template<typename t> - double dot(const CImg<t>& img) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "dot(): Empty instance.", - cimg_instance); - if (!img) - throw CImgArgumentException(_cimg_instance - "dot(): Empty specified image.", - cimg_instance); - - const ulongT nb = std::min(size(),img.size()); - double res = 0; - for (ulongT off = 0; off<nb; ++off) res+=(double)_data[off]*(double)img[off]; - return res; - } - - //! Get vector-valued pixel located at specified position. - /** - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - **/ - CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { - CImg<T> res; - if (res._height!=_spectrum) res.assign(1,_spectrum); - const ulongT whd = (ulongT)_width*_height*_depth; - const T *ptrs = data(x,y,z); - T *ptrd = res._data; - cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return res; - } - - //! Get (square) matrix-valued pixel located at specified position. - /** - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \note - The spectrum() of the image must be a square. - **/ - CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { - const int n = (int)cimg::round(std::sqrt((double)_spectrum)); - const T *ptrs = data(x,y,z,0); - const ulongT whd = (ulongT)_width*_height*_depth; - CImg<T> res(n,n); - T *ptrd = res._data; - cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } - return res; - } - - //! Get tensor-valued pixel located at specified position. - /** - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - **/ - CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { - const T *ptrs = data(x,y,z,0); - const ulongT whd = (ulongT)_width*_height*_depth; - if (_spectrum==6) - return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd)); - if (_spectrum==3) - return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd)); - return tensor(*ptrs); - } - - //! Set vector-valued pixel at specified position. - /** - \param vec Vector to put on the instance image. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - **/ - template<typename t> - CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) { - if (x<_width && y<_height && z<_depth) { - const t *ptrs = vec._data; - const ulongT whd = (ulongT)_width*_height*_depth; - T *ptrd = data(x,y,z); - for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) { - *ptrd = (T)*(ptrs++); ptrd+=whd; - } - } - return *this; - } - - //! Set (square) matrix-valued pixel at specified position. - /** - \param mat Matrix to put on the instance image. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - **/ - template<typename t> - CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { - return set_vector_at(mat,x,y,z); - } - - //! Set tensor-valued pixel at specified position. - /** - \param ten Tensor to put on the instance image. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - **/ - template<typename t> - CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { - T *ptrd = data(x,y,z,0); - const ulongT siz = (ulongT)_width*_height*_depth; - if (ten._height==2) { - *ptrd = (T)ten[0]; ptrd+=siz; - *ptrd = (T)ten[1]; ptrd+=siz; - *ptrd = (T)ten[3]; - } - else { - *ptrd = (T)ten[0]; ptrd+=siz; - *ptrd = (T)ten[1]; ptrd+=siz; - *ptrd = (T)ten[2]; ptrd+=siz; - *ptrd = (T)ten[4]; ptrd+=siz; - *ptrd = (T)ten[5]; ptrd+=siz; - *ptrd = (T)ten[8]; - } - return *this; - } - - //! Unroll pixel values along axis \c y. - /** - \note Equivalent to \code unroll('y'); \endcode. - **/ - CImg<T>& vector() { - return unroll('y'); - } - - //! Unroll pixel values along axis \c y \newinstance. - CImg<T> get_vector() const { - return get_unroll('y'); - } - - //! Resize image to become a scalar square matrix. - /** - **/ - CImg<T>& matrix() { - const ulongT siz = size(); - switch (siz) { - case 1 : break; - case 4 : _width = _height = 2; break; - case 9 : _width = _height = 3; break; - case 16 : _width = _height = 4; break; - case 25 : _width = _height = 5; break; - case 36 : _width = _height = 6; break; - case 49 : _width = _height = 7; break; - case 64 : _width = _height = 8; break; - case 81 : _width = _height = 9; break; - case 100 : _width = _height = 10; break; - default : { - ulongT i = 11, i2 = i*i; - while (i2<siz) { i2+=2*i + 1; ++i; } - if (i2==siz) _width = _height = i; - else throw CImgInstanceException(_cimg_instance - "matrix(): Invalid instance size %u (should be a square integer).", - cimg_instance, - siz); - } - } - return *this; - } - - //! Resize image to become a scalar square matrix \newinstance. - CImg<T> get_matrix() const { - return (+*this).matrix(); - } - - //! Resize image to become a symmetric tensor. - /** - **/ - CImg<T>& tensor() { - return get_tensor().move_to(*this); - } - - //! Resize image to become a symmetric tensor \newinstance. - CImg<T> get_tensor() const { - CImg<T> res; - const ulongT siz = size(); - switch (siz) { - case 1 : break; - case 3 : - res.assign(2,2); - res(0,0) = (*this)(0); - res(1,0) = res(0,1) = (*this)(1); - res(1,1) = (*this)(2); - break; - case 6 : - res.assign(3,3); - res(0,0) = (*this)(0); - res(1,0) = res(0,1) = (*this)(1); - res(2,0) = res(0,2) = (*this)(2); - res(1,1) = (*this)(3); - res(2,1) = res(1,2) = (*this)(4); - res(2,2) = (*this)(5); - break; - default : - throw CImgInstanceException(_cimg_instance - "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).", - cimg_instance); - } - return res; - } - - //! Resize image to become a diagonal matrix. - /** - \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient. - **/ - CImg<T>& diagonal() { - return get_diagonal().move_to(*this); - } - - //! Resize image to become a diagonal matrix \newinstance. - CImg<T> get_diagonal() const { - if (is_empty()) return *this; - const unsigned int siz = (unsigned int)size(); - CImg<T> res(siz,siz,1,1,0); - cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off]; - return res; - } - - //! Replace the image by an identity matrix. - /** - \note If the instance image is not square, it is resized to a square matrix using its maximum - dimension as a reference. - **/ - CImg<T>& identity_matrix() { - return identity_matrix(std::max(_width,_height)).move_to(*this); - } - - //! Replace the image by an identity matrix \newinstance. - CImg<T> get_identity_matrix() const { - return identity_matrix(std::max(_width,_height)); - } - - //! Fill image with a linear sequence of values. - /** - \param a0 Starting value of the sequence. - \param a1 Ending value of the sequence. - **/ - CImg<T>& sequence(const T& a0, const T& a1) { - if (is_empty()) return *this; - const ulongT siz = size() - 1; - T* ptr = _data; - if (siz) { - const double delta = (double)a1 - (double)a0; - cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); - } else *ptr = a0; - return *this; - } - - //! Fill image with a linear sequence of values \newinstance. - CImg<T> get_sequence(const T& a0, const T& a1) const { - return (+*this).sequence(a0,a1); - } - - //! Transpose the image, viewed as a matrix. - /** - \note Equivalent to \code permute_axes("yxzc"); \endcode. - **/ - CImg<T>& transpose() { - if (_width==1) { _width = _height; _height = 1; return *this; } - if (_height==1) { _height = _width; _width = 1; return *this; } - if (_width==_height) { - cimg_forYZC(*this,y,z,c) for (int x = y; x<width(); ++x) cimg::swap((*this)(x,y,z,c),(*this)(y,x,z,c)); - return *this; - } - return get_transpose().move_to(*this); - } - - //! Transpose the image, viewed as a matrix \newinstance. - CImg<T> get_transpose() const { - return get_permute_axes("yxzc"); - } - - //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors. - /** - \param img Image used as the second argument of the cross product. - \note The first argument of the cross product is \c *this. - **/ - template<typename t> - CImg<T>& cross(const CImg<t>& img) { - if (_width!=1 || _height<3 || img._width!=1 || img._height<3) - throw CImgInstanceException(_cimg_instance - "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.", - cimg_instance, - img._width,img._height,img._depth,img._spectrum,img._data); - - const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; - (*this)[0] = (T)(y*img[2] - z*img[1]); - (*this)[1] = (T)(z*img[0] - x*img[2]); - (*this)[2] = (T)(x*img[1] - y*img[0]); - return *this; - } - - //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance. - template<typename t> - CImg<_cimg_Tt> get_cross(const CImg<t>& img) const { - return CImg<_cimg_Tt>(*this).cross(img); - } - - //! Invert the instance image, viewed as a matrix. - /** - \param use_LU Choose the inverting algorithm. Can be: - - \c true: LU-based matrix inversion. - - \c false: SVD-based matrix inversion. - **/ - CImg<T>& invert(const bool use_LU=true) { - if (_width!=_height || _depth!=1 || _spectrum!=1) - throw CImgInstanceException(_cimg_instance - "invert(): Instance is not a square matrix.", - cimg_instance); -#ifdef cimg_use_lapack - int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N]; - Tfloat - *const lapA = new Tfloat[N*N], - *const WORK = new Tfloat[LWORK]; - cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); - cimg::getrf(N,lapA,IPIV,INFO); - if (INFO) - cimg::warn(_cimg_instance - "invert(): LAPACK function dgetrf_() returned error code %d.", - cimg_instance, - INFO); - else { - cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); - if (INFO) - cimg::warn(_cimg_instance - "invert(): LAPACK function dgetri_() returned error code %d.", - cimg_instance, - INFO); - } - if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0); - delete[] IPIV; delete[] lapA; delete[] WORK; -#else - const double dete = _width>3?-1.:det(); - if (dete!=0. && _width==2) { - const double - a = _data[0], c = _data[1], - b = _data[2], d = _data[3]; - _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete); - _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete); - } else if (dete!=0. && _width==3) { - const double - a = _data[0], d = _data[1], g = _data[2], - b = _data[3], e = _data[4], h = _data[5], - c = _data[6], f = _data[7], i = _data[8]; - _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete); - _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete); - _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete); - } else { - if (use_LU) { // LU-based inverse computation - CImg<Tfloat> A(*this,false), indx, col(1,_width); - bool d; - A._LU(indx,d); - cimg_forX(*this,j) { - col.fill(0); - col(j) = 1; - col._solve(A,indx); - cimg_forX(*this,i) (*this)(j,i) = (T)col(i); - } - } else { // SVD-based inverse computation - CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width); - SVD(U,S,V,false); - U.transpose(); - cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k]; - S.diagonal(); - *this = V*S*U; - } - } -#endif - return *this; - } - - //! Invert the instance image, viewed as a matrix \newinstance. - CImg<Tfloat> get_invert(const bool use_LU=true) const { - return CImg<Tfloat>(*this,false).invert(use_LU); - } - - //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix. - /** - **/ - CImg<T>& pseudoinvert() { - return get_pseudoinvert().move_to(*this); - } - - //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance. - CImg<Tfloat> get_pseudoinvert() const { - CImg<Tfloat> U, S, V; - SVD(U,S,V); - const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max(); - cimg_forX(V,x) { - const Tfloat s = S(x), invs = s>tolerance?1/s:0; - cimg_forY(V,y) V(x,y)*=invs; - } - return V*U.transpose(); - } - - //! Solve a system of linear equations. - /** - \param A Matrix of the linear system. - \note Solve \c AX=B where \c B=*this. - **/ - template<typename t> - CImg<T>& solve(const CImg<t>& A) { - if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1) - throw CImgArgumentException(_cimg_instance - "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have " - "incompatible dimensions.", - cimg_instance, - A._width,A._height,A._depth,A._spectrum,A._data); - typedef _cimg_Ttfloat Ttfloat; - if (A._width==A._height) { // Classical linear system - if (_width!=1) { - CImg<T> res(_width,A._width); - cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A)); - return res.move_to(*this); - } -#ifdef cimg_use_lapack - char TRANS = 'N'; - int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N]; - Ttfloat - *const lapA = new Ttfloat[N*N], - *const lapB = new Ttfloat[N], - *const WORK = new Ttfloat[LWORK]; - cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l)); - cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i)); - cimg::getrf(N,lapA,IPIV,INFO); - if (INFO) - cimg::warn(_cimg_instance - "solve(): LAPACK library function dgetrf_() returned error code %d.", - cimg_instance, - INFO); - - if (!INFO) { - cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); - if (INFO) - cimg::warn(_cimg_instance - "solve(): LAPACK library function dgetrs_() returned error code %d.", - cimg_instance, - INFO); - } - if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0); - delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; -#else - CImg<Ttfloat> lu(A,false); - CImg<Ttfloat> indx; - bool d; - lu._LU(indx,d); - _solve(lu,indx); -#endif - } else { // Least-square solution for non-square systems -#ifdef cimg_use_lapack - if (_width!=1) { - CImg<T> res(_width,A._width); - cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A)); - return res.move_to(*this); - } - char TRANS = 'N'; - int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width; - Ttfloat WORK_QUERY; - Ttfloat - * const lapA = new Ttfloat[M*N], - * const lapB = new Ttfloat[M*NRHS]; - cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO); - LWORK = (int) WORK_QUERY; - Ttfloat *const WORK = new Ttfloat[LWORK]; - cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l)); - cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l)); - cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO); - if (INFO != 0) - cimg::warn(_cimg_instance - "solve(): LAPACK library function sgels() returned error code %d.", - cimg_instance, - INFO); - assign(NRHS, N); - if (!INFO) - cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l]; - else - assign(A.get_pseudoinvert()*(*this)); - delete[] lapA; delete[] lapB; delete[] WORK; -#else - assign(A.get_pseudoinvert()*(*this)); -#endif - } - return *this; - } - - //! Solve a system of linear equations \newinstance. - template<typename t> - CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A) const { - return CImg<_cimg_Ttfloat>(*this,false).solve(A); - } - - template<typename t, typename ti> - CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) { - typedef _cimg_Ttfloat Ttfloat; - const int N = (int)size(); - int ii = -1; - Ttfloat sum; - for (int i = 0; i<N; ++i) { - const int ip = (int)indx[i]; - Ttfloat sum = (*this)(ip); - (*this)(ip) = (*this)(i); - if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j); - else if (sum!=0) ii = i; - (*this)(i) = (T)sum; - } - for (int i = N - 1; i>=0; --i) { - sum = (*this)(i); - for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j); - (*this)(i) = (T)(sum/A(i,i)); - } - return *this; - } - - //! Solve a tridiagonal system of linear equations. - /** - \param A Coefficients of the tridiagonal system. - A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ], - stored as a 3 columns matrix - \note Solve AX=B where \c B=*this, using the Thomas algorithm. - **/ - template<typename t> - CImg<T>& solve_tridiagonal(const CImg<t>& A) { - const unsigned int siz = (unsigned int)size(); - if (A._width!=3 || A._height!=siz) - throw CImgArgumentException(_cimg_instance - "solve_tridiagonal(): Instance and tridiagonal matrix " - "(%u,%u,%u,%u,%p) have incompatible dimensions.", - cimg_instance, - A._width,A._height,A._depth,A._spectrum,A._data); - typedef _cimg_Ttfloat Ttfloat; - const Ttfloat epsilon = 1e-4f; - CImg<Ttfloat> B = A.get_column(1), V(*this,false); - for (int i = 1; i<(int)siz; ++i) { - const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon); - B[i] -= m*A(2,i - 1); - V[i] -= m*V[i - 1]; - } - (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon)); - for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i + 1])/(B[i]?B[i]:epsilon)); - return *this; - } - - //! Solve a tridiagonal system of linear equations \newinstance. - template<typename t> - CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const { - return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A); - } - - //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. - /** - \param[out] val Vector of the estimated eigenvalues, in decreasing order. - \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. - **/ - template<typename t> - const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const { - if (is_empty()) { val.assign(); vec.assign(); } - else { - if (_width!=_height || _depth>1 || _spectrum>1) - throw CImgInstanceException(_cimg_instance - "eigen(): Instance is not a square matrix.", - cimg_instance); - - if (val.size()<(ulongT)_width) val.assign(1,_width); - if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width); - switch (_width) { - case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break; - case 2 : { - const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d; - double f = e*e - 4*(a*d - b*c); - if (f<0) - cimg::warn(_cimg_instance - "eigen(): Complex eigenvalues found.", - cimg_instance); - - f = std::sqrt(f); - const double - l1 = 0.5*(e - f), - l2 = 0.5*(e + f), - b2 = b*b, - norm1 = std::sqrt(cimg::sqr(l2 - a) + b2), - norm2 = std::sqrt(cimg::sqr(l1 - a) + b2); - val[0] = (t)l2; - val[1] = (t)l1; - if (norm1>0) { vec(0,0) = (t)(b/norm1); vec(0,1) = (t)((l2 - a)/norm1); } else { vec(0,0) = 1; vec(0,1) = 0; } - if (norm2>0) { vec(1,0) = (t)(b/norm2); vec(1,1) = (t)((l1 - a)/norm2); } else { vec(1,0) = 1; vec(1,1) = 0; } - } break; - default : - throw CImgInstanceException(_cimg_instance - "eigen(): Eigenvalues computation of general matrices is limited " - "to 2x2 matrices.", - cimg_instance); - } - } - return *this; - } - - //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. - /** - \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const. - **/ - CImgList<Tfloat> get_eigen() const { - CImgList<Tfloat> res(2); - eigen(res[0],res[1]); - return res; - } - - //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. - /** - \param[out] val Vector of the estimated eigenvalues, in decreasing order. - \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. - **/ - template<typename t> - const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const { - if (is_empty()) { val.assign(); vec.assign(); } - else { -#ifdef cimg_use_lapack - char JOB = 'V', UPLO = 'U'; - int N = _width, LWORK = 4*N, INFO; - Tfloat - *const lapA = new Tfloat[N*N], - *const lapW = new Tfloat[N], - *const WORK = new Tfloat[LWORK]; - cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); - cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO); - if (INFO) - cimg::warn(_cimg_instance - "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.", - cimg_instance, - INFO); - - val.assign(1,N); - vec.assign(N,N); - if (!INFO) { - cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i]; - cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]); - } else { val.fill(0); vec.fill(0); } - delete[] lapA; delete[] lapW; delete[] WORK; -#else - if (_width!=_height || _depth>1 || _spectrum>1) - throw CImgInstanceException(_cimg_instance - "eigen(): Instance is not a square matrix.", - cimg_instance); - - val.assign(1,_width); - if (vec._data) vec.assign(_width,_width); - if (_width<3) { - eigen(val,vec); - if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices - return *this; - } - CImg<t> V(_width,_width); - Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M)); - (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false); - if (maxabs!=1) val*=maxabs; - - bool is_ambiguous = false; - float eig = 0; - cimg_forY(val,p) { // check for ambiguous cases - if (val[p]>eig) eig = (float)val[p]; - t scal = 0; - cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); - if (cimg::abs(scal)<0.9f) is_ambiguous = true; - if (scal<0) val[p] = -val[p]; - } - if (is_ambiguous) { - ++(eig*=2); - SVD(vec,val,V,false,40,eig); - val-=eig; - } - CImg<intT> permutations; // sort eigenvalues in decreasing order - CImg<t> tmp(_width); - val.sort(permutations,false); - cimg_forY(vec,k) { - cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k); - std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width); - } -#endif - } - return *this; - } - - //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. - /** - \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in - symmetric_eigen(CImg<t>&,CImg<t>&) const. - **/ - CImgList<Tfloat> get_symmetric_eigen() const { - CImgList<Tfloat> res(2); - symmetric_eigen(res[0],res[1]); - return res; - } - - //! Sort pixel values and get sorting permutations. - /** - \param[out] permutations Permutation map used for the sorting. - \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. - **/ - template<typename t> - CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) { - permutations.assign(_width,_height,_depth,_spectrum); - if (is_empty()) return *this; - cimg_foroff(permutations,off) permutations[off] = (t)off; - return _quicksort(0,size() - 1,permutations,is_increasing,true); - } - - //! Sort pixel values and get sorting permutations \newinstance. - template<typename t> - CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const { - return (+*this).sort(permutations,is_increasing); - } - - //! Sort pixel values. - /** - \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. - \param axis Tells if the value sorting must be done along a specific axis. Can be: - - \c 0: All pixel values are sorted, independently on their initial position. - - \c 'x': Image columns are sorted, according to the first value in each column. - - \c 'y': Image rows are sorted, according to the first value in each row. - - \c 'z': Image slices are sorted, according to the first value in each slice. - - \c 'c': Image channels are sorted, according to the first value in each channel. - **/ - CImg<T>& sort(const bool is_increasing=true, const char axis=0) { - if (is_empty()) return *this; - CImg<uintT> perm; - switch (cimg::lowercase(axis)) { - case 0 : - _quicksort(0,size() - 1,perm,is_increasing,false); - break; - case 'x' : { - perm.assign(_width); - get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing); - CImg<T> img(*this,false); - cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c); - } break; - case 'y' : { - perm.assign(_height); - get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing); - CImg<T> img(*this,false); - cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c); - } break; - case 'z' : { - perm.assign(_depth); - get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing); - CImg<T> img(*this,false); - cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c); - } break; - case 'c' : { - perm.assign(_spectrum); - get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing); - CImg<T> img(*this,false); - cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]); - } break; - default : - throw CImgArgumentException(_cimg_instance - "sort(): Invalid specified axis '%c' " - "(should be { x | y | z | c }).", - cimg_instance,axis); - } - return *this; - } - - //! Sort pixel values \newinstance. - CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const { - return (+*this).sort(is_increasing,axis); - } - - template<typename t> - CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations, - const bool is_increasing, const bool is_permutations) { - if (indm<indM) { - const long mid = (indm + indM)/2; - if (is_increasing) { - if ((*this)[indm]>(*this)[mid]) { - cimg::swap((*this)[indm],(*this)[mid]); - if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); - } - if ((*this)[mid]>(*this)[indM]) { - cimg::swap((*this)[indM],(*this)[mid]); - if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); - } - if ((*this)[indm]>(*this)[mid]) { - cimg::swap((*this)[indm],(*this)[mid]); - if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); - } - } else { - if ((*this)[indm]<(*this)[mid]) { - cimg::swap((*this)[indm],(*this)[mid]); - if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); - } - if ((*this)[mid]<(*this)[indM]) { - cimg::swap((*this)[indM],(*this)[mid]); - if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); - } - if ((*this)[indm]<(*this)[mid]) { - cimg::swap((*this)[indm],(*this)[mid]); - if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); - } - } - if (indM - indm>=3) { - const T pivot = (*this)[mid]; - long i = indm, j = indM; - if (is_increasing) { - do { - while ((*this)[i]<pivot) ++i; - while ((*this)[j]>pivot) --j; - if (i<=j) { - if (is_permutations) cimg::swap(permutations[i],permutations[j]); - cimg::swap((*this)[i++],(*this)[j--]); - } - } while (i<=j); - } else { - do { - while ((*this)[i]>pivot) ++i; - while ((*this)[j]<pivot) --j; - if (i<=j) { - if (is_permutations) cimg::swap(permutations[i],permutations[j]); - cimg::swap((*this)[i++],(*this)[j--]); - } - } while (i<=j); - } - if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations); - if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations); - } - } - return *this; - } - - //! Compute the SVD of the instance image, viewed as a general matrix. - /** - Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices - and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V. - \param[out] U First matrix of the SVD product. - \param[out] S Coefficients of the second (diagonal) matrix of the SVD product. - These coefficients are stored as a vector. - \param[out] V Third matrix of the SVD product. - \param sorting Tells if the diagonal coefficients are sorted (in decreasing order). - \param max_iteration Maximum number of iterations considered for the algorithm convergence. - \param lambda Epsilon used for the algorithm convergence. - \note The instance matrix can be computed from \c U,\c S and \c V by - \code - const CImg<> A; // Input matrix (assumed to contain some values) - CImg<> U,S,V; - A.SVD(U,S,V) - \endcode - **/ - template<typename t> - const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true, - const unsigned int max_iteration=40, const float lambda=0) const { - if (is_empty()) { U.assign(); S.assign(); V.assign(); } - else { - U = *this; - if (lambda!=0) { - const unsigned int delta = std::min(U._width,U._height); - for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda); - } - if (S.size()<_width) S.assign(1,_width); - if (V._width<_width || V._height<_height) V.assign(_width,_width); - CImg<t> rv1(_width); - t anorm = 0, c, f, g = 0, h, s, scale = 0; - int l = 0, nm = 0; - - cimg_forX(U,i) { - l = i + 1; rv1[i] = scale*g; g = s = scale = 0; - if (i<height()) { - for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k)); - if (scale) { - for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+=U(i,k)*U(i,k); } - f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g; - for (int j = l; j<width(); ++j) { - s = 0; - for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k); - f = s/h; - for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k); - } - for (int k = i; k<height(); ++k) U(i,k)*=scale; - } - } - S[i]=scale*g; - - g = s = scale = 0; - if (i<height() && i!=width() - 1) { - for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i)); - if (scale) { - for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+=U(k,i)*U(k,i); } - f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g; - for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h; - for (int j = l; j<height(); ++j) { - s = 0; - for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i); - for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k]; - } - for (int k = l; k<width(); ++k) U(k,i)*=scale; - } - } - anorm = (t)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i]))); - } - - for (int i = width() - 1; i>=0; --i) { - if (i<width()-1) { - if (g) { - for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g; - for (int j = l; j<width(); ++j) { - s = 0; - for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k); - for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k); - } - } - for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.; - } - V(i,i) = (t)1.; g = rv1[i]; l = i; - } - - for (int i = std::min(width(),height()) - 1; i>=0; --i) { - l = i + 1; g = S[i]; - for (int j = l; j<width(); ++j) U(j,i) = 0; - if (g) { - g = 1/g; - for (int j = l; j<width(); ++j) { - s = 0; for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k); - f = (s/U(i,i))*g; - for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k); - } - for (int j = i; j<height(); ++j) U(i,j)*= g; - } else for (int j = i; j<height(); ++j) U(i,j) = 0; - ++U(i,i); - } - - for (int k = width() - 1; k>=0; --k) { - for (unsigned int its = 0; its<max_iteration; ++its) { - bool flag = true; - for (l = k; l>=1; --l) { - nm = l - 1; - if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; } - if ((cimg::abs(S[nm]) + anorm)==anorm) break; - } - if (flag) { - c = 0; s = 1; - for (int i = l; i<=k; ++i) { - f = s*rv1[i]; rv1[i] = c*rv1[i]; - if ((cimg::abs(f) + anorm)==anorm) break; - g = S[i]; h = cimg::_hypot(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h; - cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c + z*s; U(i,j) = z*c - y*s; } - } - } - - const t z = S[k]; - if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } - nm = k - 1; - t x = S[l], y = S[nm]; - g = rv1[nm]; h = rv1[k]; - f = ((y - z)*(y + z)+(g - h)*(g + h))/std::max((t)1e-25,2*h*y); - g = cimg::_hypot(f,(t)1); - f = ((x - z)*(x + z)+h*((y/(f + (f>=0?g:-g))) - h))/std::max((t)1e-25,x); - c = s = 1; - for (int j = l; j<=nm; ++j) { - const int i = j + 1; - g = rv1[i]; h = s*g; g = c*g; - t y = S[i]; - t z = cimg::_hypot(f,h); - rv1[j] = z; c = f/std::max((t)1e-25,z); s = h/std::max((t)1e-25,z); - f = x*c + g*s; g = g*c - x*s; h = y*s; y*=c; - cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c + z*s; V(i,jj) = z*c - x*s; } - z = cimg::_hypot(f,h); S[j] = z; - if (z) { z = 1/std::max((t)1e-25,z); c = f*z; s = h*z; } - f = c*g + s*y; x = c*y - s*g; - cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c + z*s; U(i,jj) = z*c - y*s; } - } - rv1[l] = 0; rv1[k]=f; S[k]=x; - } - } - - if (sorting) { - CImg<intT> permutations; - CImg<t> tmp(_width); - S.sort(permutations,false); - cimg_forY(U,k) { - cimg_forY(permutations,y) tmp(y) = U(permutations(y),k); - std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width); - } - cimg_forY(V,k) { - cimg_forY(permutations,y) tmp(y) = V(permutations(y),k); - std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width); - } - } - } - return *this; - } - - //! Compute the SVD of the instance image, viewed as a general matrix. - /** - \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in - SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const. - **/ - CImgList<Tfloat> get_SVD(const bool sorting=true, - const unsigned int max_iteration=40, const float lambda=0) const { - CImgList<Tfloat> res(3); - SVD(res[0],res[1],res[2],sorting,max_iteration,lambda); - return res; - } - - // [internal] Compute the LU decomposition of a permuted matrix. - template<typename t> - CImg<T>& _LU(CImg<t>& indx, bool& d) { - const int N = width(); - int imax = 0; - CImg<Tfloat> vv(N); - indx.assign(N); - d = true; - cimg_forX(*this,i) { - Tfloat vmax = 0; - cimg_forX(*this,j) { - const Tfloat tmp = cimg::abs((*this)(j,i)); - if (tmp>vmax) vmax = tmp; - } - if (vmax==0) { indx.fill(0); return fill(0); } - vv[i] = 1/vmax; - } - cimg_forX(*this,j) { - for (int i = 0; i<j; ++i) { - Tfloat sum=(*this)(j,i); - for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k); - (*this)(j,i) = (T)sum; - } - Tfloat vmax = 0; - for (int i = j; i<width(); ++i) { - Tfloat sum=(*this)(j,i); - for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k); - (*this)(j,i) = (T)sum; - const Tfloat tmp = vv[i]*cimg::abs(sum); - if (tmp>=vmax) { vmax=tmp; imax=i; } - } - if (j!=imax) { - cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); - d =!d; - vv[imax] = vv[j]; - } - indx[j] = (t)imax; - if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; - if (j<N) { - const Tfloat tmp = 1/(Tfloat)(*this)(j,j); - for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp); - } - } - return *this; - } - - //! Compute minimal path in a graph, using the Dijkstra algorithm. - /** - \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance - between two nodes (i,j). - \param nb_nodes Number of graph nodes. - \param starting_node Indice of the starting node. - \param ending_node Indice of the ending node (set to ~0U to ignore ending node). - \param previous_node Array that gives the previous node indice in the path to the starting node - (optional parameter). - \return Array of distances of each node to the starting node. - **/ - template<typename tf, typename t> - static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes, - const unsigned int starting_node, const unsigned int ending_node, - CImg<t>& previous_node) { - if (starting_node>=nb_nodes) - throw CImgArgumentException("CImg<%s>::dijkstra(): Specified indice of starting node %u is higher " - "than number of nodes %u.", - pixel_type(),starting_node,nb_nodes); - CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max()); - dist(starting_node) = 0; - previous_node.assign(1,nb_nodes,1,1,(t)-1); - previous_node(starting_node) = (t)starting_node; - CImg<uintT> Q(nb_nodes); - cimg_forX(Q,u) Q(u) = (unsigned int)u; - cimg::swap(Q(starting_node),Q(0)); - unsigned int sizeQ = nb_nodes; - while (sizeQ) { - // Update neighbors from minimal vertex - const unsigned int umin = Q(0); - if (umin==ending_node) sizeQ = 0; - else { - const T dmin = dist(umin); - const T infty = cimg::type<T>::max(); - for (unsigned int q = 1; q<sizeQ; ++q) { - const unsigned int v = Q(q); - const T d = (T)distance(v,umin); - if (d<infty) { - const T alt = dmin + d; - if (alt<dist(v)) { - dist(v) = alt; - previous_node(v) = (t)umin; - const T distpos = dist(Q(q)); - for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par) - cimg::swap(Q(pos),Q(par)); - } - } - } - // Remove minimal vertex from queue - Q(0) = Q(--sizeQ); - const T distpos = dist(Q(0)); - for (unsigned int pos = 0, left = 0, right = 0; - ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) || - (right<sizeQ && distpos>dist(Q(right)));) { - if (right<sizeQ) { - if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; } - else { cimg::swap(Q(pos),Q(right)); pos = right; } - } else { cimg::swap(Q(pos),Q(left)); pos = left; } - } - } - } - return dist; - } - - //! Return minimal path in a graph, using the Dijkstra algorithm. - template<typename tf, typename t> - static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes, - const unsigned int starting_node, const unsigned int ending_node=~0U) { - CImg<uintT> foo; - return dijkstra(distance,nb_nodes,starting_node,ending_node,foo); - } - - //! Return minimal path in a graph, using the Dijkstra algorithm. - /** - \param starting_node Indice of the starting node. - \param ending_node Indice of the ending node. - \param previous_node Array that gives the previous node indice in the path to the starting node - (optional parameter). - \return Array of distances of each node to the starting node. - \note image instance corresponds to the adjacency matrix of the graph. - **/ - template<typename t> - CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node, - CImg<t>& previous_node) { - return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this); - } - - //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. - template<typename t> - CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, - CImg<t>& previous_node) const { - if (_width!=_height || _depth!=1 || _spectrum!=1) - throw CImgInstanceException(_cimg_instance - "dijkstra(): Instance is not a graph adjacency matrix.", - cimg_instance); - - return dijkstra(*this,_width,starting_node,ending_node,previous_node); - } - - //! Return minimal path in a graph, using the Dijkstra algorithm. - CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { - return get_dijkstra(starting_node,ending_node).move_to(*this); - } - - //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. - CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { - CImg<uintT> foo; - return get_dijkstra(starting_node,ending_node,foo); - } - - //! Return an image containing the Ascii codes of the specified string. - /** - \param str input C-string to encode as an image. - \param is_last_zero Tells if the ending \c '0' character appear in the resulting image. - \param is_shared Return result that shares its buffer with \p str. - **/ - static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) { - if (!str) return CImg<T>(); - return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared); - } - - //! Return a \c 1x1 image containing specified value. - /** - \param a0 First vector value. - **/ - static CImg<T> vector(const T& a0) { - CImg<T> r(1,1); - r[0] = a0; - return r; - } - - //! Return a \c 1x2 image containing specified values. - /** - \param a0 First vector value. - \param a1 Second vector value. - **/ - static CImg<T> vector(const T& a0, const T& a1) { - CImg<T> r(1,2); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; - return r; - } - - //! Return a \c 1x3 image containing specified values. - /** - \param a0 First vector value. - \param a1 Second vector value. - \param a2 Third vector value. - **/ - static CImg<T> vector(const T& a0, const T& a1, const T& a2) { - CImg<T> r(1,3); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; - return r; - } - - //! Return a \c 1x4 image containing specified values. - /** - \param a0 First vector value. - \param a1 Second vector value. - \param a2 Third vector value. - \param a3 Fourth vector value. - **/ - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) { - CImg<T> r(1,4); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - return r; - } - - //! Return a \c 1x5 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { - CImg<T> r(1,5); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; - return r; - } - - //! Return a \c 1x6 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { - CImg<T> r(1,6); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; - return r; - } - - //! Return a \c 1x7 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6) { - CImg<T> r(1,7); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; - return r; - } - - //! Return a \c 1x8 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7) { - CImg<T> r(1,8); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - return r; - } - - //! Return a \c 1x9 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8) { - CImg<T> r(1,9); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; - return r; - } - - //! Return a \c 1x10 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9) { - CImg<T> r(1,10); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; - return r; - } - - //! Return a \c 1x11 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9, const T& a10) { - CImg<T> r(1,11); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; - return r; - } - - //! Return a \c 1x12 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9, const T& a10, const T& a11) { - CImg<T> r(1,12); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; - return r; - } - - //! Return a \c 1x13 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9, const T& a10, const T& a11, - const T& a12) { - CImg<T> r(1,13); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; - *(ptr++) = a12; - return r; - } - - //! Return a \c 1x14 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9, const T& a10, const T& a11, - const T& a12, const T& a13) { - CImg<T> r(1,14); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; - *(ptr++) = a12; *(ptr++) = a13; - return r; - } - - //! Return a \c 1x15 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9, const T& a10, const T& a11, - const T& a12, const T& a13, const T& a14) { - CImg<T> r(1,15); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; - *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; - return r; - } - - //! Return a \c 1x16 image containing specified values. - static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9, const T& a10, const T& a11, - const T& a12, const T& a13, const T& a14, const T& a15) { - CImg<T> r(1,16); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; - *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; - return r; - } - - //! Return a 1x1 matrix containing specified coefficients. - /** - \param a0 First matrix value. - \note Equivalent to vector(const T&). - **/ - static CImg<T> matrix(const T& a0) { - return vector(a0); - } - - //! Return a 2x2 matrix containing specified coefficients. - /** - \param a0 First matrix value. - \param a1 Second matrix value. - \param a2 Third matrix value. - \param a3 Fourth matrix value. - **/ - static CImg<T> matrix(const T& a0, const T& a1, - const T& a2, const T& a3) { - CImg<T> r(2,2); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; - *(ptr++) = a2; *(ptr++) = a3; - return r; - } - - //! Return a 3x3 matrix containing specified coefficients. - /** - \param a0 First matrix value. - \param a1 Second matrix value. - \param a2 Third matrix value. - \param a3 Fourth matrix value. - \param a4 Fifth matrix value. - \param a5 Sixth matrix value. - \param a6 Seventh matrix value. - \param a7 Eighth matrix value. - \param a8 Nineth matrix value. - **/ - static CImg<T> matrix(const T& a0, const T& a1, const T& a2, - const T& a3, const T& a4, const T& a5, - const T& a6, const T& a7, const T& a8) { - CImg<T> r(3,3); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; - *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; - *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; - return r; - } - - //! Return a 4x4 matrix containing specified coefficients. - static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, - const T& a4, const T& a5, const T& a6, const T& a7, - const T& a8, const T& a9, const T& a10, const T& a11, - const T& a12, const T& a13, const T& a14, const T& a15) { - CImg<T> r(4,4); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; - *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; - *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; - *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; - return r; - } - - //! Return a 5x5 matrix containing specified coefficients. - static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, - const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, - const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, - const T& a15, const T& a16, const T& a17, const T& a18, const T& a19, - const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) { - CImg<T> r(5,5); T *ptr = r._data; - *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; - *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; - *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; - *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; - *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; - return r; - } - - //! Return a 1x1 symmetric matrix containing specified coefficients. - /** - \param a0 First matrix value. - \note Equivalent to vector(const T&). - **/ - static CImg<T> tensor(const T& a0) { - return matrix(a0); - } - - //! Return a 2x2 symmetric matrix tensor containing specified coefficients. - static CImg<T> tensor(const T& a0, const T& a1, const T& a2) { - return matrix(a0,a1,a1,a2); - } - - //! Return a 3x3 symmetric matrix containing specified coefficients. - static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { - return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5); - } - - //! Return a 1x1 diagonal matrix containing specified coefficients. - static CImg<T> diagonal(const T& a0) { - return matrix(a0); - } - - //! Return a 2x2 diagonal matrix containing specified coefficients. - static CImg<T> diagonal(const T& a0, const T& a1) { - return matrix(a0,0,0,a1); - } - - //! Return a 3x3 diagonal matrix containing specified coefficients. - static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) { - return matrix(a0,0,0,0,a1,0,0,0,a2); - } - - //! Return a 4x4 diagonal matrix containing specified coefficients. - static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { - return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); - } - - //! Return a 5x5 diagonal matrix containing specified coefficients. - static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { - return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4); - } - - //! Return a NxN identity matrix. - /** - \param N Dimension of the matrix. - **/ - static CImg<T> identity_matrix(const unsigned int N) { - CImg<T> res(N,N,1,1,0); - cimg_forX(res,x) res(x,x) = 1; - return res; - } - - //! Return a N-numbered sequence vector from \p a0 to \p a1. - /** - \param N Size of the resulting vector. - \param a0 Starting value of the sequence. - \param a1 Ending value of the sequence. - **/ - static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) { - if (N) return CImg<T>(1,N).sequence(a0,a1); - return CImg<T>(); - } - - //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion. - /** - \param x X-coordinate of the rotation axis, or first quaternion coordinate. - \param y Y-coordinate of the rotation axis, or second quaternion coordinate. - \param z Z-coordinate of the rotation axis, or third quaternion coordinate. - \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate. - \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w). - **/ - static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w, - const bool is_quaternion=false) { - double X, Y, Z, W, N; - if (is_quaternion) { - N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w); - if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; } - else { X = Y = Z = 0; W = 1; } - return CImg<T>::matrix((T)(X*X + Y*Y - Z*Z - W*W),(T)(2*Y*Z - 2*X*W),(T)(2*X*Z + 2*Y*W), - (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y), - (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W)); - } - N = cimg::hypot((double)x,(double)y,(double)z); - if (N>0) { X = x/N; Y = y/N; Z = z/N; } - else { X = Y = 0; Z = 1; } - const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang); - return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s), - (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s), - (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c)); - } - - //@} - //----------------------------------- - // - //! \name Value Manipulation - //@{ - //----------------------------------- - - //! Fill all pixel values with specified value. - /** - \param val Fill value. - **/ - CImg<T>& fill(const T& val) { - if (is_empty()) return *this; - if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val; - else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*) - return *this; - } - - //! Fill all pixel values with specified value \newinstance. - CImg<T> get_fill(const T& val) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val); - } - - //! Fill sequentially all pixel values with specified values. - /** - \param val0 First fill value. - \param val1 Second fill value. - **/ - CImg<T>& fill(const T& val0, const T& val1) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 1; - for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; } - if (ptrd!=ptre + 1) *(ptrd++) = val0; - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 2; - for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; } - ptre+=2; - switch (ptre - ptrd) { - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 3; - for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; } - ptre+=3; - switch (ptre - ptrd) { - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 4; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; - } - ptre+=4; - switch (ptre - ptrd) { - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 5; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - } - ptre+=5; - switch (ptre - ptrd) { - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 6; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - *(ptrd++) = val6; - } - ptre+=6; - switch (ptre - ptrd) { - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 7; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; - *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; - } - ptre+=7; - switch (ptre - ptrd) { - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 8; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; - *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; - } - ptre+=8; - switch (ptre - ptrd) { - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 9; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; - *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; - } - ptre+=9; - switch (ptre - ptrd) { - case 9 : *(--ptre) = val8; // fallthrough - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 10; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; - *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; - *(ptrd++) = val10; - } - ptre+=10; - switch (ptre - ptrd) { - case 10 : *(--ptre) = val9; // fallthrough - case 9 : *(--ptre) = val8; // fallthrough - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 11; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11; - } - ptre+=11; - switch (ptre - ptrd) { - case 11 : *(--ptre) = val10; // fallthrough - case 10 : *(--ptre) = val9; // fallthrough - case 9 : *(--ptre) = val8; // fallthrough - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, - val11); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 12; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11; - *(ptrd++) = val12; - } - ptre+=12; - switch (ptre - ptrd) { - case 12 : *(--ptre) = val11; // fallthrough - case 11 : *(--ptre) = val10; // fallthrough - case 10 : *(--ptre) = val9; // fallthrough - case 9 : *(--ptre) = val8; // fallthrough - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, - val11,val12); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12, const T& val13) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 13; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11; - *(ptrd++) = val12; *(ptrd++) = val13; - } - ptre+=13; - switch (ptre - ptrd) { - case 13 : *(--ptre) = val12; // fallthrough - case 12 : *(--ptre) = val11; // fallthrough - case 11 : *(--ptre) = val10; // fallthrough - case 10 : *(--ptre) = val9; // fallthrough - case 9 : *(--ptre) = val8; // fallthrough - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12, const T& val13) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, - val11,val12,val13); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12, const T& val13, const T& val14) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 14; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11; - *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; - } - ptre+=14; - switch (ptre - ptrd) { - case 14 : *(--ptre) = val13; // fallthrough - case 13 : *(--ptre) = val12; // fallthrough - case 12 : *(--ptre) = val11; // fallthrough - case 11 : *(--ptre) = val10; // fallthrough - case 10 : *(--ptre) = val9; // fallthrough - case 9 : *(--ptre) = val8; // fallthrough - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12, const T& val13, const T& val14) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, - val11,val12,val13,val14); - } - - //! Fill sequentially all pixel values with specified values \overloading. - CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12, const T& val13, const T& val14, const T& val15) { - if (is_empty()) return *this; - T *ptrd, *ptre = end() - 15; - for (ptrd = _data; ptrd<ptre; ) { - *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; - *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11; - *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15; - } - ptre+=15; - switch (ptre - ptrd) { - case 15 : *(--ptre) = val14; // fallthrough - case 14 : *(--ptre) = val13; // fallthrough - case 13 : *(--ptre) = val12; // fallthrough - case 12 : *(--ptre) = val11; // fallthrough - case 11 : *(--ptre) = val10; // fallthrough - case 10 : *(--ptre) = val9; // fallthrough - case 9 : *(--ptre) = val8; // fallthrough - case 8 : *(--ptre) = val7; // fallthrough - case 7 : *(--ptre) = val6; // fallthrough - case 6 : *(--ptre) = val5; // fallthrough - case 5 : *(--ptre) = val4; // fallthrough - case 4 : *(--ptre) = val3; // fallthrough - case 3 : *(--ptre) = val2; // fallthrough - case 2 : *(--ptre) = val1; // fallthrough - case 1 : *(--ptre) = val0; // fallthrough - } - return *this; - } - - //! Fill sequentially all pixel values with specified values \newinstance. - CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, - const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, - const T& val12, const T& val13, const T& val14, const T& val15) const { - return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, - val11,val12,val13,val14,val15); - } - - //! Fill sequentially pixel values according to a given expression. - /** - \param expression C-string describing a math formula, or a sequence of values. - \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling. - \param allow_formula Tells that mathematical formulas are authorized for the filling. - \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression. - \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression. - **/ - CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) { - return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0); - } - - // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula | - // 2 = allow formula but do not fill image values }. - CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode, - const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs, - const char *const calling_function, const CImg<T> *provides_copy) { - if (is_empty() || !expression || !*expression) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - CImg<charT> is_error; - bool is_value_sequence = false; - cimg_abort_init; - - if (formula_mode) { - - // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser. - double value; - char sep; - const int err = cimg_sscanf(expression,"%lf %c",&value,&sep); - if (err==1 || (err==2 && sep==',')) { - if (err==1) return fill((T)value); - else is_value_sequence = true; - } - - // Try to fill values according to a formula. - _cimg_abort_init_omp; - if (!is_value_sequence) try { - CImg<T> base = provides_copy?provides_copy->get_shared():get_shared(); - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || - *expression=='*' || *expression==':'), - calling_function,base,this,list_inputs,list_outputs,true); - if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && - mp.need_input_copy) - base.assign().assign(*this,false); // Needs input copy - - bool do_in_parallel = false; -#ifdef cimg_use_openmp - cimg_openmp_if(*expression=='*' || *expression==':' || - (mp.is_parallelizable && _width>=(cimg_openmp_sizefactor)*320 && - _height*_depth*_spectrum>=2)) - do_in_parallel = true; -#endif - if (mp.result_dim) { // Vector-valued expression - const unsigned int N = std::min(mp.result_dim,_spectrum); - const ulongT whd = (ulongT)_width*_height*_depth; - T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; - if (*expression=='<') { - CImg<doubleT> res(1,mp.result_dim); - cimg_rofYZ(*this,y,z) { - cimg_abort_test; - if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0); - else cimg_rofX(*this,x) { - mp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } - } else if (*expression=='>' || !do_in_parallel) { - CImg<doubleT> res(1,mp.result_dim); - cimg_forYZ(*this,y,z) { - cimg_abort_test; - if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0); - else cimg_forX(*this,x) { - mp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } - } else { -#ifdef cimg_use_openmp - cimg_pragma_openmp(parallel) - { - _cimg_math_parser - _mp = omp_get_thread_num()?mp:_cimg_math_parser(), - &lmp = omp_get_thread_num()?_mp:mp; - lmp.is_fill = true; - cimg_pragma_openmp(for collapse(2)) - cimg_forYZ(*this,y,z) _cimg_abort_try_omp { - cimg_abort_test; - if (formula_mode==2) cimg_forX(*this,x) lmp(x,y,z,0); - else { - CImg<doubleT> res(1,lmp.result_dim); - T *ptrd = data(0,y,z,0); - cimg_forX(*this,x) { - lmp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } - } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp - } -#endif - } - - } else { // Scalar-valued expression - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') { - if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); } - else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } - } else if (*expression=='>' || !do_in_parallel) { - if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); } - else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } - } else { -#ifdef cimg_use_openmp - cimg_pragma_openmp(parallel) - { - _cimg_math_parser - _mp = omp_get_thread_num()?mp:_cimg_math_parser(), - &lmp = omp_get_thread_num()?_mp:mp; - lmp.is_fill = true; - cimg_pragma_openmp(for collapse(3)) - cimg_forYZC(*this,y,z,c) _cimg_abort_try_omp { - cimg_abort_test; - if (formula_mode==2) cimg_forX(*this,x) lmp(x,y,z,c); - else { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); - } - } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp - } -#endif - } - } - mp.end(); - } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error); } - } - - // Try to fill values according to a value sequence. - if (!formula_mode || is_value_sequence || is_error) { - CImg<charT> item(256); - char sep = 0; - const char *nexpression = expression; - ulongT nb = 0; - const ulongT siz = size(); - T *ptrd = _data; - for (double val = 0; *nexpression && nb<siz; ++nb) { - sep = 0; - const int err = cimg_sscanf(nexpression,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep); - if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { - nexpression+=std::strlen(item) + (err>1); - *(ptrd++) = (T)val; - } else break; - } - cimg::exception_mode(omode); - if (nb<siz && (sep || *nexpression)) { - if (is_error) throw CImgArgumentException("%s",is_error._data); - else throw CImgArgumentException(_cimg_instance - "%s(): Invalid sequence of filling values '%s'.", - cimg_instance,calling_function,expression); - } - if (repeat_values && nb && nb<siz) - for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs; - } - - cimg::exception_mode(omode); - cimg_abort_test; - return *this; - } - - //! Fill sequentially pixel values according to a given expression \newinstance. - CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, - const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const { - return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs); - } - - //! Fill sequentially pixel values according to the values found in another image. - /** - \param values Image containing the values used for the filling. - \param repeat_values In case there are less values than necessary in \c values, tells if these values must be - repeated for the filling. - **/ - template<typename t> - CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) { - if (is_empty() || !values) return *this; - T *ptrd = _data, *ptre = ptrd + size(); - for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs) - *(ptrd++) = (T)*ptrs; - if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs; - return *this; - } - - //! Fill sequentially pixel values according to the values found in another image \newinstance. - template<typename t> - CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const { - return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values): - (+*this).fill(values,repeat_values); - } - - //! Fill pixel values along the X-axis at a specified pixel position. - /** - \param y Y-coordinate of the filled column. - \param z Z-coordinate of the filled column. - \param c C-coordinate of the filled column. - \param a0 First fill value. - **/ - CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) { -#define _cimg_fill1(x,y,z,c,off,siz,t) { \ - va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \ - for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \ - va_end(ap); } - if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int); - return *this; - } - - //! Fill pixel values along the X-axis at a specified pixel position \overloading. - CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) { - if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double); - return *this; - } - - //! Fill pixel values along the Y-axis at a specified pixel position. - /** - \param x X-coordinate of the filled row. - \param z Z-coordinate of the filled row. - \param c C-coordinate of the filled row. - \param a0 First fill value. - **/ - CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) { - if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int); - return *this; - } - - //! Fill pixel values along the Y-axis at a specified pixel position \overloading. - CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) { - if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double); - return *this; - } - - //! Fill pixel values along the Z-axis at a specified pixel position. - /** - \param x X-coordinate of the filled slice. - \param y Y-coordinate of the filled slice. - \param c C-coordinate of the filled slice. - \param a0 First fill value. - **/ - CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) { - const ulongT wh = (ulongT)_width*_height; - if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int); - return *this; - } - - //! Fill pixel values along the Z-axis at a specified pixel position \overloading. - CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) { - const ulongT wh = (ulongT)_width*_height; - if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double); - return *this; - } - - //! Fill pixel values along the C-axis at a specified pixel position. - /** - \param x X-coordinate of the filled channel. - \param y Y-coordinate of the filled channel. - \param z Z-coordinate of the filled channel. - \param a0 First filling value. - **/ - CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { - const ulongT whd = (ulongT)_width*_height*_depth; - if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int); - return *this; - } - - //! Fill pixel values along the C-axis at a specified pixel position \overloading. - CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { - const ulongT whd = (ulongT)_width*_height*_depth; - if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double); - return *this; - } - - //! Discard specified sequence of values in the image buffer, along a specific axis. - /** - \param values Sequence of values to discard. - \param axis Axis along which the values are discarded. If set to \c 0 (default value) - the method does it for all the buffer values and returns a one-column vector. - \note Discarded values will change the image geometry, so the resulting image - is returned as a one-column vector. - **/ - template<typename t> - CImg<T>& discard(const CImg<t>& values, const char axis=0) { - if (is_empty() || !values) return *this; - return get_discard(values,axis).move_to(*this); - } - - template<typename t> - CImg<T> get_discard(const CImg<t>& values, const char axis=0) const { - CImg<T> res; - if (!values) return +*this; - if (is_empty()) return res; - const ulongT vsiz = values.size(); - const char _axis = cimg::lowercase(axis); - ulongT j = 0; - unsigned int k = 0; - int i0 = 0; - res.assign(width(),height(),depth(),spectrum()); - switch (_axis) { - case 'x' : { - cimg_forX(*this,i) { - if ((*this)(i)!=(T)values[j]) { - if (j) --i; - res.draw_image(k,get_columns(i0,i)); - k+=i - i0 + 1; i0 = i + 1; j = 0; - } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; } - res.resize(k,-100,-100,-100,0); - } break; - case 'y' : { - cimg_forY(*this,i) { - if ((*this)(0,i)!=(T)values[j]) { - if (j) --i; - res.draw_image(0,k,get_rows(i0,i)); - k+=i - i0 + 1; i0 = i + 1; j = 0; - } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; } - res.resize(-100,k,-100,-100,0); - } break; - case 'z' : { - cimg_forZ(*this,i) { - if ((*this)(0,0,i)!=(T)values[j]) { - if (j) --i; - res.draw_image(0,0,k,get_slices(i0,i)); - k+=i - i0 + 1; i0 = i + 1; j = 0; - } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; } - res.resize(-100,-100,k,-100,0); - } break; - case 'c' : { - cimg_forC(*this,i) { - if ((*this)(0,0,0,i)!=(T)values[j]) { - if (j) --i; - res.draw_image(0,0,0,k,get_channels(i0,i)); - k+=i - i0 + 1; i0 = i + 1; j = 0; - } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } - } - if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; } - res.resize(-100,-100,-100,k,0); - } break; - default : { - res.unroll('y'); - cimg_foroff(*this,i) { - if ((*this)[i]!=(T)values[j]) { - if (j) --i; - std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T)); - k+=i - i0 + 1; i0 = (int)i + 1; j = 0; - } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }} - } - const ulongT siz = size(); - if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; } - res.resize(1,k,1,1,0); - } - } - return res; - } - - //! Discard neighboring duplicates in the image buffer, along the specified axis. - CImg<T>& discard(const char axis=0) { - return get_discard(axis).move_to(*this); - } - - //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance. - CImg<T> get_discard(const char axis=0) const { - CImg<T> res; - if (is_empty()) return res; - const char _axis = cimg::lowercase(axis); - T current = *_data?(T)0:(T)1; - int j = 0; - res.assign(width(),height(),depth(),spectrum()); - switch (_axis) { - case 'x' : { - cimg_forX(*this,i) - if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); } - res.resize(j,-100,-100,-100,0); - } break; - case 'y' : { - cimg_forY(*this,i) - if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); } - res.resize(-100,j,-100,-100,0); - } break; - case 'z' : { - cimg_forZ(*this,i) - if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); } - res.resize(-100,-100,j,-100,0); - } break; - case 'c' : { - cimg_forC(*this,i) - if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); } - res.resize(-100,-100,-100,j,0); - } break; - default : { - res.unroll('y'); - cimg_foroff(*this,i) - if ((*this)[i]!=current) res[j++] = current = (*this)[i]; - res.resize(-100,j,-100,-100,0); - } - } - return res; - } - - //! Invert endianness of all pixel values. - /** - **/ - CImg<T>& invert_endianness() { - cimg::invert_endianness(_data,size()); - return *this; - } - - //! Invert endianness of all pixel values \newinstance. - CImg<T> get_invert_endianness() const { - return (+*this).invert_endianness(); - } - - //! Fill image with random values in specified range. - /** - \param val_min Minimal authorized random value. - \param val_max Maximal authorized random value. - \note Random variables are uniformely distributed in [val_min,val_max]. - **/ - CImg<T>& rand(const T& val_min, const T& val_max) { - const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1); - if (cimg::type<T>::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng)); - cimg::srand(rng); - } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng))); - cimg::srand(rng); - } - return *this; - } - - //! Fill image with random values in specified range \newinstance. - CImg<T> get_rand(const T& val_min, const T& val_max) const { - return (+*this).rand(val_min,val_max); - } - - //! Round pixel values. - /** - \param y Rounding precision. - \param rounding_type Rounding type. Can be: - - \c -1: Backward. - - \c 0: Nearest. - - \c 1: Forward. - **/ - CImg<T>& round(const double y=1, const int rounding_type=0) { - if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192); - return *this; - } - - //! Round pixel values \newinstance. - CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const { - return (+*this).round(y,rounding_type); - } - - //! Add random noise to pixel values. - /** - \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the - global value range. - \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper, - \p 3=Poisson or \p 4=Rician). - \return A reference to the modified image instance. - \note - - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on - the image value itself. - - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance. - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_noise(40); - (img,res.normalize(0,255)).display(); - \endcode - \image html ref_noise.jpg - **/ - CImg<T>& noise(const double sigma, const unsigned int noise_type=0) { - if (is_empty()) return *this; - const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max(); - Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0; - if (nsigma==0 && noise_type!=3) return *this; - if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M); - if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.); - switch (noise_type) { - case 0 : { // Gaussian noise - cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_rofoff(*this,off) { - Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng)); - if (val>vmax) val = vmax; - if (val<vmin) val = vmin; - _data[off] = (T)val; - } - cimg::srand(rng); - } - } break; - case 1 : { // Uniform noise - cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_rofoff(*this,off) { - Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::rand(-1,1,&rng)); - if (val>vmax) val = vmax; - if (val<vmin) val = vmin; - _data[off] = (T)val; - } - cimg::srand(rng); - } - } break; - case 2 : { // Salt & Pepper noise - if (nsigma<0) nsigma = -nsigma; - if (M==m) { - if (cimg::type<T>::is_float()) { --m; ++M; } - else { m = (Tfloat)cimg::type<T>::min(); M = (Tfloat)cimg::type<T>::max(); } - } - cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_rofoff(*this,off) if (cimg::rand(100,&rng)<nsigma) _data[off] = (T)(cimg::rand(1,&rng)<0.5?M:m); - cimg::srand(rng); - } - } break; - case 3 : { // Poisson Noise - cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_rofoff(*this,off) _data[off] = (T)cimg::prand(_data[off],&rng); - cimg::srand(rng); - } - } break; - case 4 : { // Rice noise - const Tfloat sqrt2 = (Tfloat)std::sqrt(2.); - cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_rofoff(*this,off) { - const Tfloat - val0 = (Tfloat)_data[off]/sqrt2, - re = (Tfloat)(val0 + nsigma*cimg::grand(&rng)), - im = (Tfloat)(val0 + nsigma*cimg::grand(&rng)); - Tfloat val = cimg::hypot(re,im); - if (val>vmax) val = vmax; - if (val<vmin) val = vmin; - _data[off] = (T)val; - } - cimg::srand(rng); - } - } break; - default : - throw CImgArgumentException(_cimg_instance - "noise(): Invalid specified noise type %d " - "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).", - cimg_instance, - noise_type); - } - return *this; - } - - //! Add random noise to pixel values \newinstance. - CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const { - return (+*this).noise(sigma,noise_type); - } - - //! Linearly normalize pixel values. - /** - \param min_value Minimum desired value of the resulting image. - \param max_value Maximum desired value of the resulting image. - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220); - (img,res).display(); - \endcode - \image html ref_normalize2.jpg - **/ - CImg<T>& normalize(const T& min_value, const T& max_value) { - if (is_empty()) return *this; - const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value; - T m, M = max_min(m); - const Tfloat fm = (Tfloat)m, fM = (Tfloat)M; - if (m==M) return fill(min_value); - if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a); - return *this; - } - - //! Linearly normalize pixel values \newinstance. - CImg<Tfloat> get_normalize(const T& min_value, const T& max_value) const { - return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value); - } - - //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm. - /** - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_normalize(); - (img,res.normalize(0,255)).display(); - \endcode - \image html ref_normalize.jpg - **/ - CImg<T>& normalize() { - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth>=16)) - cimg_forYZ(*this,y,z) { - T *ptrd = data(0,y,z,0); - cimg_forX(*this,x) { - const T *ptrs = ptrd; - float n = 0; - cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; } - n = (float)std::sqrt(n); - T *_ptrd = ptrd++; - if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; } - else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; } - } - } - return *this; - } - - //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance. - CImg<Tfloat> get_normalize() const { - return CImg<Tfloat>(*this,false).normalize(); - } - - //! Compute Lp-norm of each multi-valued pixel of the image instance. - /** - \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0). - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_norm(); - (img,res.normalize(0,255)).display(); - \endcode - \image html ref_norm.jpg - **/ - CImg<T>& norm(const int norm_type=2) { - if (_spectrum==1 && norm_type) return abs(); - return get_norm(norm_type).move_to(*this); - } - - //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance. - CImg<Tfloat> get_norm(const int norm_type=2) const { - if (is_empty()) return *this; - if (_spectrum==1 && norm_type) return get_abs(); - const ulongT whd = (ulongT)_width*_height*_depth; - CImg<Tfloat> res(_width,_height,_depth); - switch (norm_type) { - case -1 : { // Linf-norm - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth>=16)) - cimg_forYZ(*this,y,z) { - const ulongT off = (ulongT)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; } - *(ptrd++) = n; - } - } - } break; - case 0 : { // L0-norm - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth>=16)) - cimg_forYZ(*this,y,z) { - const ulongT off = (ulongT)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - unsigned int n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; } - *(ptrd++) = (Tfloat)n; - } - } - } break; - case 1 : { // L1-norm - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth>=16)) - cimg_forYZ(*this,y,z) { - const ulongT off = (ulongT)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; } - *(ptrd++) = n; - } - } - } break; - case 2 : { // L2-norm - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth>=16)) - cimg_forYZ(*this,y,z) { - const ulongT off = (ulongT)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; } - *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); - } - } - } break; - default : { // Linf-norm - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth>=16)) - cimg_forYZ(*this,y,z) { - const ulongT off = (ulongT)offset(0,y,z); - const T *ptrs = _data + off; - Tfloat *ptrd = res._data + off; - cimg_forX(*this,x) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; } - *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type); - } - } - } - } - return res; - } - - //! Cut pixel values in specified range. - /** - \param min_value Minimum desired value of the resulting image. - \param max_value Maximum desired value of the resulting image. - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_cut(160,220); - (img,res).display(); - \endcode - \image html ref_cut.jpg - **/ - CImg<T>& cut(const T& min_value, const T& max_value) { - if (is_empty()) return *this; - const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value; - cimg_openmp_for(*this,cimg::cut(*ptr,a,b),32768); - return *this; - } - - //! Cut pixel values in specified range \newinstance. - CImg<T> get_cut(const T& min_value, const T& max_value) const { - return (+*this).cut(min_value,max_value); - } - - //! Uniformly quantize pixel values. - /** - \param nb_levels Number of quantization levels. - \param keep_range Tells if resulting values keep the same range as the original ones. - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_quantize(4); - (img,res).display(); - \endcode - \image html ref_quantize.jpg - **/ - CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) { - if (!nb_levels) - throw CImgArgumentException(_cimg_instance - "quantize(): Invalid quantization request with 0 values.", - cimg_instance); - - if (is_empty()) return *this; - Tfloat m, M = (Tfloat)max_min(m), range = M - m; - if (range>0) { - if (keep_range) - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) - cimg_rofoff(*this,off) { - const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); - _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels); - } else - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) - cimg_rofoff(*this,off) { - const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); - _data[off] = (T)std::min(val,nb_levels - 1); - } - } - return *this; - } - - //! Uniformly quantize pixel values \newinstance. - CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const { - return (+*this).quantize(n,keep_range); - } - - //! Threshold pixel values. - /** - \param value Threshold value - \param soft_threshold Tells if soft thresholding must be applied (instead of hard one). - \param strict_threshold Tells if threshold value is strict. - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_threshold(128); - (img,res.normalize(0,255)).display(); - \endcode - \image html ref_threshold.jpg - **/ - CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) { - if (is_empty()) return *this; - if (strict_threshold) { - if (soft_threshold) - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) - cimg_rofoff(*this,off) { - const T v = _data[off]; - _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0; - } - else - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) - cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0; - } else { - if (soft_threshold) - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) - cimg_rofoff(*this,off) { - const T v = _data[off]; - _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0; - } - else - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) - cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0; - } - return *this; - } - - //! Threshold pixel values \newinstance. - CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const { - return (+*this).threshold(value,soft_threshold,strict_threshold); - } - - //! Compute the histogram of pixel values. - /** - \param nb_levels Number of desired histogram levels. - \param min_value Minimum pixel value considered for the histogram computation. - All pixel values lower than \p min_value will not be counted. - \param max_value Maximum pixel value considered for the histogram computation. - All pixel values higher than \p max_value will not be counted. - \note - - The histogram H of an image I is the 1D function where H(x) counts the number of occurences of the value x - in the image I. - - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional. - \par Example - \code - const CImg<float> img = CImg<float>("reference.jpg").histogram(256); - img.display_graph(0,3); - \endcode - \image html ref_histogram.jpg - **/ - CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) { - return get_histogram(nb_levels,min_value,max_value).move_to(*this); - } - - //! Compute the histogram of pixel values \overloading. - CImg<T>& histogram(const unsigned int nb_levels) { - return get_histogram(nb_levels).move_to(*this); - } - - //! Compute the histogram of pixel values \newinstance. - CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const { - if (!nb_levels || is_empty()) return CImg<ulongT>(); - const double - vmin = (double)(min_value<max_value?min_value:max_value), - vmax = (double)(min_value<max_value?max_value:min_value); - CImg<ulongT> res(nb_levels,1,1,1,0); - cimg_rof(*this,ptrs,T) { - const T val = *ptrs; - if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))]; - } - return res; - } - - //! Compute the histogram of pixel values \newinstance. - CImg<ulongT> get_histogram(const unsigned int nb_levels) const { - if (!nb_levels || is_empty()) return CImg<ulongT>(); - T vmax = 0, vmin = min_max(vmax); - return get_histogram(nb_levels,vmin,vmax); - } - - //! Equalize histogram of pixel values. - /** - \param nb_levels Number of histogram levels used for the equalization. - \param min_value Minimum pixel value considered for the histogram computation. - All pixel values lower than \p min_value will not be counted. - \param max_value Maximum pixel value considered for the histogram computation. - All pixel values higher than \p max_value will not be counted. - \par Example - \code - const CImg<float> img("reference.jpg"), res = img.get_equalize(256); - (img,res).display(); - \endcode - \image html ref_equalize.jpg - **/ - CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) { - if (!nb_levels || is_empty()) return *this; - const T - vmin = min_value<max_value?min_value:max_value, - vmax = min_value<max_value?max_value:min_value; - CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax); - ulongT cumul = 0; - cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } - if (!cumul) cumul = 1; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576)) - cimg_rofoff(*this,off) { - const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin)); - if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul); - } - return *this; - } - - //! Equalize histogram of pixel values \overloading. - CImg<T>& equalize(const unsigned int nb_levels) { - if (!nb_levels || is_empty()) return *this; - T vmax = 0, vmin = min_max(vmax); - return equalize(nb_levels,vmin,vmax); - } - - //! Equalize histogram of pixel values \newinstance. - CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const { - return (+*this).equalize(nblevels,val_min,val_max); - } - - //! Equalize histogram of pixel values \newinstance. - CImg<T> get_equalize(const unsigned int nblevels) const { - return (+*this).equalize(nblevels); - } - - //! Index multi-valued pixels regarding to a specified colormap. - /** - \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing. - \param dithering Level of dithering (0=disable, 1=standard level). - \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors. - \note - - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>. - \par Example - \code - const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255); - const CImg<float> res = img.get_index(colormap,1,true); - (img,res).display(); - \endcode - \image html ref_index.jpg - **/ - template<typename t> - CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) { - return get_index(colormap,dithering,map_indexes).move_to(*this); - } - - //! Index multi-valued pixels regarding to a specified colormap \newinstance. - template<typename t> - CImg<typename CImg<t>::Tuint> - get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const { - if (colormap._spectrum!=_spectrum) - throw CImgArgumentException(_cimg_instance - "index(): Instance and specified colormap (%u,%u,%u,%u,%p) " - "have incompatible dimensions.", - cimg_instance, - colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); - - typedef typename CImg<t>::Tuint tuint; - if (is_empty()) return CImg<tuint>(); - const ulongT - whd = (ulongT)_width*_height*_depth, - pwhd = (ulongT)colormap._width*colormap._height*colormap._depth; - CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1); - tuint *ptrd = res._data; - if (dithering>0) { // Dithered versions - const float ndithering = cimg::cut(dithering,0,1)/16; - Tfloat valm = 0, valM = (Tfloat)max_min(valm); - if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; } - CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1); - Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0); - const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth; - switch (_spectrum) { - case 1 : { // Optimized for scalars - cimg_forYZ(*this,y,z) { - if (y<height() - 2) { - Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0); - cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++); - } - Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next; - cimg_forX(*this,x) { - const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0; - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) { - const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0; - if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; } - } - const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering; - *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0; - if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data); - } - cimg::swap(cache_current,cache_next); - } - } break; - case 2 : { // Optimized for 2D vectors - tuint *ptrd1 = ptrd + whd; - cimg_forYZ(*this,y,z) { - if (y<height() - 2) { - Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd; - const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd; - cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); } - } - Tfloat - *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, - *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd; - cimg_forX(*this,x) { - const Tfloat - _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0, - _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1; - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) { - const Tfloat - pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, - dist = pval0*pval0 + pval1*pval1; - if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; } - } - const t *const ptrmin1 = ptrmin0 + pwhd; - const Tfloat - err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering, - err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering; - *ptrs0+=7*err0; *ptrs1+=7*err1; - *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; - *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; - *ptrsn0+=err0; *ptrsn1+=err1; - if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; } - else *(ptrd++) = (tuint)(ptrmin0 - colormap._data); - } - cimg::swap(cache_current,cache_next); - } - } break; - case 3 : { // Optimized for 3D vectors (colors) - tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd; - cimg_forYZ(*this,y,z) { - if (y<height() - 2) { - Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd; - const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd; - cimg_forX(*this,x) { - *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++); - } - } - Tfloat - *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd, - *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd; - cimg_forX(*this,x) { - const Tfloat - _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0, - _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1, - _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2; - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, - *ptrp_end = ptrp1; ptrp0<ptrp_end; ) { - const Tfloat - pval0 = (Tfloat)*(ptrp0++) - val0, - pval1 = (Tfloat)*(ptrp1++) - val1, - pval2 = (Tfloat)*(ptrp2++) - val2, - dist = pval0*pval0 + pval1*pval1 + pval2*pval2; - if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; } - } - const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd; - const Tfloat - err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering, - err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering, - err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering; - - *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2; - *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2; - *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2; - *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2; - - if (map_indexes) { - *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2; - } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data); - } - cimg::swap(cache_current,cache_next); - } - } break; - default : // Generic version - cimg_forYZ(*this,y,z) { - if (y<height() - 2) { - Tfloat *ptrc = cache_next; - cimg_forC(*this,c) { - Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c); - cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++); - ptrc+=cwhd; - } - } - Tfloat *ptrs = cache_current, *ptrsn = cache_next; - cimg_forX(*this,x) { - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data; - for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) { - Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp; - cimg_forC(*this,c) { - const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val; - dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd; - } - if (dist<distmin) { ptrmin = ptrp; distmin = dist; } - } - const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1; - cimg_forC(*this,c) { - const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering; - *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err; - _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2; - } - if (map_indexes) { - tuint *_ptrd = ptrd++; - cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; } - } - else *(ptrd++) = (tuint)(ptrmin - colormap._data); - } - cimg::swap(cache_current,cache_next); - } - } - } else { // Non-dithered versions - switch (_spectrum) { - case 1 : { // Optimized for scalars - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 && - _height*_depth>=16 && pwhd>=16)) - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z); - for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) { - const Tfloat val0 = (Tfloat)*(ptrs0++); - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) { - const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0; - if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; } - } - if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data); - } - } - } break; - case 2 : { // Optimized for 2D vectors - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 && - _height*_depth>=16 && pwhd>=16)) - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd; - for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) { - const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++); - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) { - const Tfloat - pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, - dist = pval0*pval0 + pval1*pval1; - if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; } - } - if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); } - else *(ptrd++) = (tuint)(ptrmin0 - colormap._data); - } - } - } break; - case 3 : { // Optimized for 3D vectors (colors) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 && - _height*_depth>=16 && pwhd>=16)) - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd; - for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, - *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) { - const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++); - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data; - for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, - *ptrp_end = ptrp1; ptrp0<ptrp_end; ) { - const Tfloat - pval0 = (Tfloat)*(ptrp0++) - val0, - pval1 = (Tfloat)*(ptrp1++) - val1, - pval2 = (Tfloat)*(ptrp2++) - val2, - dist = pval0*pval0 + pval1*pval1 + pval2*pval2; - if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; } - } - if (map_indexes) { - *(ptrd++) = (tuint)*ptrmin0; - *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); - *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd); - } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data); - } - } - } break; - default : // Generic version - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 && - _height*_depth>=16 && pwhd>=16)) - cimg_forYZ(*this,y,z) { - tuint *ptrd = res.data(0,y,z); - for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) { - Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data; - for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) { - Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp; - cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; } - if (dist<distmin) { ptrmin = ptrp; distmin = dist; } - } - if (map_indexes) { - tuint *_ptrd = ptrd++; - cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; } - } - else *(ptrd++) = (tuint)(ptrmin - colormap._data); - } - } - } - } - return res; - } - - //! Map predefined colormap on the scalar (indexed) image instance. - /** - \param colormap Multi-valued colormap used for mapping the indexes. - \param boundary_conditions The border condition type { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. - \par Example - \code - const CImg<float> img("reference.jpg"), - colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), - colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), - res = img.get_index(colormap1,0).map(colormap2); - (img,res).display(); - \endcode - \image html ref_map.jpg - **/ - template<typename t> - CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) { - return get_map(colormap,boundary_conditions).move_to(*this); - } - - //! Map predefined colormap on the scalar (indexed) image instance \newinstance. - template<typename t> - CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const { - if (_spectrum!=1 && colormap._spectrum!=1) - throw CImgArgumentException(_cimg_instance - "map(): Instance and specified colormap (%u,%u,%u,%u,%p) " - "have incompatible dimensions.", - cimg_instance, - colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); - - const ulongT - whd = (ulongT)_width*_height*_depth, - cwhd = (ulongT)colormap._width*colormap._height*colormap._depth, - cwhd2 = 2*cwhd; - CImg<t> res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum); - switch (colormap._spectrum) { - - case 1 : { // Optimized for scalars - const T *ptrs = _data; - switch (boundary_conditions) { - case 3 : // Mirror - cimg_for(res,ptrd,t) { - const ulongT ind = ((ulongT)*(ptrs++))%cwhd2; - *ptrd = colormap[ind<cwhd?ind:cwhd2 - ind - 1]; - } - break; - case 2 : // Periodic - cimg_for(res,ptrd,t) { - const ulongT ind = (ulongT)*(ptrs++); - *ptrd = colormap[ind%cwhd]; - } break; - case 1 : // Neumann - cimg_for(res,ptrd,t) { - const longT ind = (longT)*(ptrs++); - *ptrd = colormap[cimg::cut(ind,(longT)0,(longT)cwhd - 1)]; - } break; - default : // Dirichlet - cimg_for(res,ptrd,t) { - const ulongT ind = (ulongT)*(ptrs++); - *ptrd = ind<cwhd?colormap[ind]:(t)0; - } - } - } break; - - case 2 : { // Optimized for 2D vectors - const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd; - t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd; - switch (boundary_conditions) { - case 3 : // Mirror - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT - _ind = ((ulongT)*(ptrs++))%cwhd2, - ind = _ind<cwhd?_ind:cwhd2 - _ind - 1; - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; - } - break; - case 2 : // Periodic - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT ind = ((ulongT)*(ptrs++))%cwhd; - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; - } - break; - case 1 : // Neumann - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1); - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; - } - break; - default : // Dirichlet - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT ind = (ulongT)*(ptrs++); - const bool is_in = ind<cwhd; - *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0; - } - } - } break; - - case 3 : { // Optimized for 3D vectors (colors) - const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp1 + cwhd; - t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd; - switch (boundary_conditions) { - case 3 : // Mirror - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT - _ind = ((ulongT)*(ptrs++))%cwhd2, - ind = _ind<cwhd?_ind:cwhd2 - _ind - 1; - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind]; - } break; - case 2 : // Periodic - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT ind = ((ulongT)*(ptrs++))%cwhd; - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind]; - } break; - case 1 : // Neumann - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1); - *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind]; - } break; - default : // Dirichlet - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT ind = (ulongT)*(ptrs++); - const bool is_in = ind<cwhd; - *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0; *(ptrd2++) = is_in?ptrp2[ind]:(t)0; - } - } - } break; - - default : { // Generic version - t *ptrd = res._data; - switch (boundary_conditions) { - case 3 : // Mirror - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT - _ind = ((ulongT)*(ptrs++))%cwhd, - ind = _ind<cwhd?_ind:cwhd2 - _ind - 1; - const t *ptrp = colormap._data + ind; - t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; } - } break; - case 2 : // Periodic - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT ind = ((ulongT)*(ptrs++))%cwhd; - const t *ptrp = colormap._data + ind; - t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; } - } break; - case 1 : // Neumann - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1); - const t *ptrp = colormap._data + ind; - t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; } - } break; - default : // Dirichlet - for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) { - const ulongT ind = (ulongT)*(ptrs++); - const bool is_in = ind<cwhd; - if (is_in) { - const t *ptrp = colormap._data + ind; - t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; } - } else { - t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = (t)0; _ptrd+=whd; } - } - } - } - } - } - return res; - } - - //! Label connected components. - /** - \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity - in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case. - \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region. - \note The algorithm of connected components computation has been primarily done - by A. Meijster, according to the publication: - 'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.", - In: Science of Computer Programming 41 (2001), pp. 173--194'. - The submitted code has then been modified to fit CImg coding style and constraints. - **/ - CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) { - return get_label(is_high_connectivity,tolerance).move_to(*this); - } - - //! Label connected components \newinstance. - CImg<ulongT> get_label(const bool is_high_connectivity=false, - const Tfloat tolerance=0) const { - if (is_empty()) return CImg<ulongT>(); - - // Create neighborhood tables. - int dx[13], dy[13], dz[13], nb = 0; - dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0; - dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0; - if (is_high_connectivity) { - dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0; - dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0; - } - if (_depth>1) { // 3D version - dx[nb] = 0; dy[nb] = 0; dz[nb++]=1; - if (is_high_connectivity) { - dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1; - dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1; - dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1; - dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1; - - dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1; - dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1; - dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1; - dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1; - } - } - return _label(nb,dx,dy,dz,tolerance); - } - - //! Label connected components \overloading. - /** - \param connectivity_mask Mask of the neighboring pixels. - \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region. - **/ - template<typename t> - CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) { - return get_label(connectivity_mask,tolerance).move_to(*this); - } - - //! Label connected components \newinstance. - template<typename t> - CImg<ulongT> get_label(const CImg<t>& connectivity_mask, - const Tfloat tolerance=0) const { - int nb = 0; - cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb; - CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0); - nb = 0; - cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) && - connectivity_mask(x,y,z)) { - dx[nb] = x; dy[nb] = y; dz[nb++] = z; - } - return _label(nb,dx,dy,dz,tolerance); - } - - CImg<ulongT> _label(const unsigned int nb, const int *const dx, - const int *const dy, const int *const dz, - const Tfloat tolerance) const { - CImg<ulongT> res(_width,_height,_depth,_spectrum); - cimg_forC(*this,c) { - CImg<ulongT> _res = res.get_shared_channel(c); - - // Init label numbers. - ulongT *ptr = _res.data(); - cimg_foroff(_res,p) *(ptr++) = p; - - // For each neighbour-direction, label. - for (unsigned int n = 0; n<nb; ++n) { - const int _dx = dx[n], _dy = dy[n], _dz = dz[n]; - if (_dx || _dy || _dz) { - const int - x0 = _dx<0?-_dx:0, - x1 = _dx<0?width():width() - _dx, - y0 = _dy<0?-_dy:0, - y1 = _dy<0?height():height() - _dy, - z0 = _dz<0?-_dz:0, - z1 = _dz<0?depth():depth() - _dz; - const longT - wh = (longT)width()*height(), - whd = (longT)width()*height()*depth(), - offset = _dz*wh + _dy*width() + _dx; - for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) { - for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) { - for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) { - if (cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd))<=tolerance) { - const longT q = p + offset; - ulongT x, y; - for (x = (ulongT)(p<q?q:p), y = (ulongT)(p<q?p:q); x!=y && _res[x]!=x; ) { - x = _res[x]; if (x<y) cimg::swap(x,y); - } - if (x!=y) _res[x] = (ulongT)y; - for (ulongT _p = (ulongT)p; _p!=y; ) { - const ulongT h = _res[_p]; - _res[_p] = (ulongT)y; - _p = h; - } - for (ulongT _q = (ulongT)q; _q!=y; ) { - const ulongT h = _res[_q]; - _res[_q] = (ulongT)y; - _q = h; - } - } - } - } - } - } - } - - // Resolve equivalences. - ulongT counter = 0; - ptr = _res.data(); - cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; } - } - return res; - } - - // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version. - CImg<T>& _system_strescape() { -#define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\ - move_to(list); \ - CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break - CImgList<T> list; - const T *ptrs = _data; - cimg_for(*this,p,T) switch ((int)*p) { - cimg_system_strescape('\\',"\\\\"); - cimg_system_strescape('\"',"\\\""); - cimg_system_strescape('!',"\"\\!\""); - cimg_system_strescape('`',"\\`"); - cimg_system_strescape('$',"\\$"); - } - if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list); - return (list>'x').move_to(*this); - } - - //@} - //--------------------------------- - // - //! \name Color Base Management - //@{ - //--------------------------------- - - //! Return colormap \e "default", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_default.jpg - **/ - static const CImg<Tuchar>& default_LUT256() { - static CImg<Tuchar> colormap; - cimg::mutex(8); - if (!colormap) { - colormap.assign(1,256,1,3); - for (unsigned int index = 0, r = 16; r<256; r+=32) - for (unsigned int g = 16; g<256; g+=32) - for (unsigned int b = 32; b<256; b+=64) { - colormap(0,index,0) = (Tuchar)r; - colormap(0,index,1) = (Tuchar)g; - colormap(0,index++,2) = (Tuchar)b; - } - } - cimg::mutex(8,0); - return colormap; - } - - //! Return colormap \e "HSV", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_hsv.jpg - **/ - static const CImg<Tuchar>& HSV_LUT256() { - static CImg<Tuchar> colormap; - cimg::mutex(8); - if (!colormap) { - CImg<Tint> tmp(1,256,1,3,1); - tmp.get_shared_channel(0).sequence(0,359); - colormap = tmp.HSVtoRGB(); - } - cimg::mutex(8,0); - return colormap; - } - - //! Return colormap \e "lines", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_lines.jpg - **/ - static const CImg<Tuchar>& lines_LUT256() { - static const unsigned char pal[] = { - 217,62,88,75,1,237,240,12,56,160,165,116,1,1,204,2,15,248,148,185,133,141,46,246,222,116,16,5,207,226, - 17,114,247,1,214,53,238,0,95,55,233,235,109,0,17,54,33,0,90,30,3,0,94,27,19,0,68,212,166,130,0,15,7,119, - 238,2,246,198,0,3,16,10,13,2,25,28,12,6,2,99,18,141,30,4,3,140,12,4,30,233,7,10,0,136,35,160,168,184,20, - 233,0,1,242,83,90,56,180,44,41,0,6,19,207,5,31,214,4,35,153,180,75,21,76,16,202,218,22,17,2,136,71,74, - 81,251,244,148,222,17,0,234,24,0,200,16,239,15,225,102,230,186,58,230,110,12,0,7,129,249,22,241,37,219, - 1,3,254,210,3,212,113,131,197,162,123,252,90,96,209,60,0,17,0,180,249,12,112,165,43,27,229,77,40,195,12, - 87,1,210,148,47,80,5,9,1,137,2,40,57,205,244,40,8,252,98,0,40,43,206,31,187,0,180,1,69,70,227,131,108,0, - 223,94,228,35,248,243,4,16,0,34,24,2,9,35,73,91,12,199,51,1,249,12,103,131,20,224,2,70,32, - 233,1,165,3,8,154,246,233,196,5,0,6,183,227,247,195,208,36,0,0,226,160,210,198,69,153,210,1,23,8,192,2,4, - 137,1,0,52,2,249,241,129,0,0,234,7,238,71,7,32,15,157,157,252,158,2,250,6,13,30,11,162,0,199,21,11,27,224, - 4,157,20,181,111,187,218,3,0,11,158,230,196,34,223,22,248,135,254,210,157,219,0,117,239,3,255,4,227,5,247, - 11,4,3,188,111,11,105,195,2,0,14,1,21,219,192,0,183,191,113,241,1,12,17,248,0,48,7,19,1,254,212,0,239,246, - 0,23,0,250,165,194,194,17,3,253,0,24,6,0,141,167,221,24,212,2,235,243,0,0,205,1,251,133,204,28,4,6,1,10, - 141,21,74,12,236,254,228,19,1,0,214,1,186,13,13,6,13,16,27,209,6,216,11,207,251,59,32,9,155,23,19,235,143, - 116,6,213,6,75,159,23,6,0,228,4,10,245,249,1,7,44,234,4,102,174,0,19,239,103,16,15,18,8,214,22,4,47,244, - 255,8,0,251,173,1,212,252,250,251,252,6,0,29,29,222,233,246,5,149,0,182,180,13,151,0,203,183,0,35,149,0, - 235,246,254,78,9,17,203,73,11,195,0,3,5,44,0,0,237,5,106,6,130,16,214,20,168,247,168,4,207,11,5,1,232,251, - 129,210,116,231,217,223,214,27,45,38,4,177,186,249,7,215,172,16,214,27,249,230,236,2,34,216,217,0,175,30, - 243,225,244,182,20,212,2,226,21,255,20,0,2,13,62,13,191,14,76,64,20,121,4,118,0,216,1,147,0,2,210,1,215, - 95,210,236,225,184,46,0,248,24,11,1,9,141,250,243,9,221,233,160,11,147,2,55,8,23,12,253,9,0,54,0,231,6,3, - 141,8,2,246,9,180,5,11,8,227,8,43,110,242,1,130,5,97,36,10,6,219,86,133,11,108,6,1,5,244,67,19,28,0,174, - 154,16,127,149,252,188,196,196,228,244,9,249,0,0,0,37,170,32,250,0,73,255,23,3,224,234,38,195,198,0,255,87, - 33,221,174,31,3,0,189,228,6,153,14,144,14,108,197,0,9,206,245,254,3,16,253,178,248,0,95,125,8,0,3,168,21, - 23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 }; - static const CImg<Tuchar> colormap(pal,1,256,1,3,false); - return colormap; - } - - //! Return colormap \e "hot", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_hot.jpg - **/ - static const CImg<Tuchar>& hot_LUT256() { - static CImg<Tuchar> colormap; - cimg::mutex(8); - if (!colormap) { - colormap.assign(1,4,1,3,(T)0); - colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255; - colormap.resize(1,256,1,3,3); - } - cimg::mutex(8,0); - return colormap; - } - - //! Return colormap \e "cool", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_cool.jpg - **/ - static const CImg<Tuchar>& cool_LUT256() { - static CImg<Tuchar> colormap; - cimg::mutex(8); - if (!colormap) colormap.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3); - cimg::mutex(8,0); - return colormap; - } - - //! Return colormap \e "jet", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_jet.jpg - **/ - static const CImg<Tuchar>& jet_LUT256() { - static CImg<Tuchar> colormap; - cimg::mutex(8); - if (!colormap) { - colormap.assign(1,4,1,3,(T)0); - colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255; - colormap.resize(1,256,1,3,3); - } - cimg::mutex(8,0); - return colormap; - } - - //! Return colormap \e "flag", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_flag.jpg - **/ - static const CImg<Tuchar>& flag_LUT256() { - static CImg<Tuchar> colormap; - cimg::mutex(8); - if (!colormap) { - colormap.assign(1,4,1,3,(T)0); - colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255; - colormap.resize(1,256,1,3,0,2); - } - cimg::mutex(8,0); - return colormap; - } - - //! Return colormap \e "cube", containing 256 colors entries in RGB. - /** - \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_cube.jpg - **/ - static const CImg<Tuchar>& cube_LUT256() { - static CImg<Tuchar> colormap; - cimg::mutex(8); - if (!colormap) { - colormap.assign(1,8,1,3,(T)0); - colormap[1] = colormap[3] = colormap[5] = colormap[7] = - colormap[10] = colormap[11] = colormap[12] = colormap[13] = - colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255; - colormap.resize(1,256,1,3,3); - } - cimg::mutex(8,0); - return colormap; - } - - //! Convert pixel values from sRGB to RGB color spaces. - CImg<T>& sRGBtoRGB() { - if (is_empty()) return *this; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) - cimg_rofoff(*this,off) { - const Tfloat - sval = (Tfloat)_data[off]/255, - val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f)); - _data[off] = (T)cimg::cut(val*255,0,255); - } - return *this; - } - - //! Convert pixel values from sRGB to RGB color spaces \newinstance. - CImg<Tfloat> get_sRGBtoRGB() const { - return CImg<Tfloat>(*this,false).sRGBtoRGB(); - } - - //! Convert pixel values from RGB to sRGB color spaces. - CImg<T>& RGBtosRGB() { - if (is_empty()) return *this; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) - cimg_rofoff(*this,off) { - const Tfloat - val = (Tfloat)_data[off]/255, - sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f); - _data[off] = (T)cimg::cut(sval*255,0,255); - } - return *this; - } - - //! Convert pixel values from RGB to sRGB color spaces \newinstance. - CImg<Tfloat> get_RGBtosRGB() const { - return CImg<Tfloat>(*this,false).RGBtosRGB(); - } - - //! Convert pixel values from RGB to HSI color spaces. - CImg<T>& RGBtoHSI() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoHSI(): Instance is not a RGB image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - R = (Tfloat)p1[N], - G = (Tfloat)p2[N], - B = (Tfloat)p3[N], - theta = (Tfloat)(std::acos(0.5f*((R - G) + (R - B))/ - std::sqrt(cimg::sqr(R - G) + (R - B)*(G - B)))*180/cimg::PI), - m = cimg::min(R,G,B), - sum = R + G + B; - Tfloat H = 0, S = 0, I = 0; - if (theta>0) H = B<=G?theta:360 - theta; - if (sum>0) S = 1 - 3*m/sum; - I = sum/(3*255); - p1[N] = (T)cimg::cut(H,0,360); - p2[N] = (T)cimg::cut(S,0,1); - p3[N] = (T)cimg::cut(I,0,1); - } - return *this; - } - - //! Convert pixel values from RGB to HSI color spaces \newinstance. - CImg<Tfloat> get_RGBtoHSI() const { - return CImg<Tfloat>(*this,false).RGBtoHSI(); - } - - //! Convert pixel values from HSI to RGB color spaces. - CImg<T>& HSItoRGB() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "HSItoRGB(): Instance is not a HSI image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) - for (ulongT N = 0; N<whd; ++N) { - Tfloat - H = cimg::mod((Tfloat)p1[N],(Tfloat)360), - S = (Tfloat)p2[N], - I = (Tfloat)p3[N], - a = I*(1 - S), - R = 0, G = 0, B = 0; - if (H<120) { - B = a; - R = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180))); - G = 3*I - (R + B); - } else if (H<240) { - H-=120; - R = a; - G = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180))); - B = 3*I - (R + G); - } else { - H-=240; - G = a; - B = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180))); - R = 3*I - (G + B); - } - p1[N] = (T)cimg::cut(R*255,0,255); - p2[N] = (T)cimg::cut(G*255,0,255); - p3[N] = (T)cimg::cut(B*255,0,255); - } - return *this; - } - - //! Convert pixel values from HSI to RGB color spaces \newinstance. - CImg<Tfloat> get_HSItoRGB() const { - return CImg< Tuchar>(*this,false).HSItoRGB(); - } - - //! Convert pixel values from RGB to HSL color spaces. - CImg<T>& RGBtoHSL() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoHSL(): Instance is not a RGB image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - R = (Tfloat)p1[N], - G = (Tfloat)p2[N], - B = (Tfloat)p3[N], - m = cimg::min(R,G,B), - M = cimg::max(R,G,B), - L = (m + M)/(2*255); - Tfloat H = 0, S = 0; - if (M!=m) { - const Tfloat - f = R==m?G - B:G==m?B - R:R - G, - i = R==m?3:G==m?5:1; - H = i - f/(M - m); - if (H>=6) H-=6; - H*=60; - S = 2*L<=1?(M - m)/(M + m):(M - m)/(2*255 - M - m); - } - p1[N] = (T)cimg::cut(H,0,360); - p2[N] = (T)cimg::cut(S,0,1); - p3[N] = (T)cimg::cut(L,0,1); - } - return *this; - } - - //! Convert pixel values from RGB to HSL color spaces \newinstance. - CImg<Tfloat> get_RGBtoHSL() const { - return CImg<Tfloat>(*this,false).RGBtoHSL(); - } - - //! Convert pixel values from HSL to RGB color spaces. - CImg<T>& HSLtoRGB() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "HSLtoRGB(): Instance is not a HSL image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - H = cimg::mod((Tfloat)p1[N],(Tfloat)360), - S = (Tfloat)p2[N], - L = (Tfloat)p3[N], - q = 2*L<1?L*(1 + S):L + S - L*S, - p = 2*L - q, - h = H/360, - tr = h + (Tfloat)1/3, - tg = h, - tb = h - (Tfloat)1/3, - ntr = tr<0?tr + 1:tr>1?tr - 1:(Tfloat)tr, - ntg = tg<0?tg + 1:tg>1?tg - 1:(Tfloat)tg, - ntb = tb<0?tb + 1:tb>1?tb - 1:(Tfloat)tb, - R = 6*ntr<1?p + (q - p)*6*ntr:2*ntr<1?q:3*ntr<2?p + (q - p)*6*(2.f/3 - ntr):p, - G = 6*ntg<1?p + (q - p)*6*ntg:2*ntg<1?q:3*ntg<2?p + (q - p)*6*(2.f/3 - ntg):p, - B = 6*ntb<1?p + (q - p)*6*ntb:2*ntb<1?q:3*ntb<2?p + (q - p)*6*(2.f/3 - ntb):p; - p1[N] = (T)cimg::cut(255*R,0,255); - p2[N] = (T)cimg::cut(255*G,0,255); - p3[N] = (T)cimg::cut(255*B,0,255); - } - return *this; - } - - //! Convert pixel values from HSL to RGB color spaces \newinstance. - CImg<Tuchar> get_HSLtoRGB() const { - return CImg<Tuchar>(*this,false).HSLtoRGB(); - } - - //! Convert pixel values from RGB to HSV color spaces. - CImg<T>& RGBtoHSV() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoHSV(): Instance is not a RGB image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - R = (Tfloat)p1[N], - G = (Tfloat)p2[N], - B = (Tfloat)p3[N], - m = cimg::min(R,G,B), - M = cimg::max(R,G,B); - Tfloat H = 0, S = 0; - if (M!=m) { - const Tfloat - f = R==m?G - B:G==m?B - R:R - G, - i = R==m?3:G==m?5:1; - H = i - f/(M - m); - if (H>=6) H-=6; - H*=60; - S = (M - m)/M; - } - p1[N] = (T)cimg::cut(H,0,360); - p2[N] = (T)cimg::cut(S,0,1); - p3[N] = (T)cimg::cut(M/255,0,1); - } - return *this; - } - - //! Convert pixel values from RGB to HSV color spaces \newinstance. - CImg<Tfloat> get_RGBtoHSV() const { - return CImg<Tfloat>(*this,false).RGBtoHSV(); - } - - //! Convert pixel values from HSV to RGB color spaces. - CImg<T>& HSVtoRGB() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "HSVtoRGB(): Instance is not a HSV image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) - for (ulongT N = 0; N<whd; ++N) { - Tfloat - H = cimg::mod((Tfloat)p1[N],(Tfloat)360), - S = (Tfloat)p2[N], - V = (Tfloat)p3[N], - R = 0, G = 0, B = 0; - if (H==0 && S==0) R = G = B = V; - else { - H/=60; - const int i = (int)std::floor(H); - const Tfloat - f = (i&1)?H - i:1 - H + i, - m = V*(1 - S), - n = V*(1 - S*f); - switch (i) { - case 6 : - case 0 : R = V; G = n; B = m; break; - case 1 : R = n; G = V; B = m; break; - case 2 : R = m; G = V; B = n; break; - case 3 : R = m; G = n; B = V; break; - case 4 : R = n; G = m; B = V; break; - case 5 : R = V; G = m; B = n; break; - } - } - p1[N] = (T)cimg::cut(R*255,0,255); - p2[N] = (T)cimg::cut(G*255,0,255); - p3[N] = (T)cimg::cut(B*255,0,255); - } - return *this; - } - - //! Convert pixel values from HSV to RGB color spaces \newinstance. - CImg<Tuchar> get_HSVtoRGB() const { - return CImg<Tuchar>(*this,false).HSVtoRGB(); - } - - //! Convert pixel values from RGB to YCbCr color spaces. - CImg<T>& RGBtoYCbCr() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoYCbCr(): Instance is not a RGB image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - R = (Tfloat)p1[N], - G = (Tfloat)p2[N], - B = (Tfloat)p3[N], - Y = (66*R + 129*G + 25*B + 128)/256 + 16, - Cb = (-38*R - 74*G + 112*B + 128)/256 + 128, - Cr = (112*R - 94*G - 18*B + 128)/256 + 128; - p1[N] = (T)cimg::cut(Y,0,255), - p2[N] = (T)cimg::cut(Cb,0,255), - p3[N] = (T)cimg::cut(Cr,0,255); - } - return *this; - } - - //! Convert pixel values from RGB to YCbCr color spaces \newinstance. - CImg<Tuchar> get_RGBtoYCbCr() const { - return CImg<Tuchar>(*this,false).RGBtoYCbCr(); - } - - //! Convert pixel values from RGB to YCbCr color spaces. - CImg<T>& YCbCrtoRGB() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "YCbCrtoRGB(): Instance is not a YCbCr image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - Y = (Tfloat)p1[N] - 16, - Cb = (Tfloat)p2[N] - 128, - Cr = (Tfloat)p3[N] - 128, - R = (298*Y + 409*Cr + 128)/256, - G = (298*Y - 100*Cb - 208*Cr + 128)/256, - B = (298*Y + 516*Cb + 128)/256; - p1[N] = (T)cimg::cut(R,0,255), - p2[N] = (T)cimg::cut(G,0,255), - p3[N] = (T)cimg::cut(B,0,255); - } - return *this; - } - - //! Convert pixel values from RGB to YCbCr color spaces \newinstance. - CImg<Tuchar> get_YCbCrtoRGB() const { - return CImg<Tuchar>(*this,false).YCbCrtoRGB(); - } - - //! Convert pixel values from RGB to YUV color spaces. - CImg<T>& RGBtoYUV() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoYUV(): Instance is not a RGB image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - R = (Tfloat)p1[N]/255, - G = (Tfloat)p2[N]/255, - B = (Tfloat)p3[N]/255, - Y = 0.299f*R + 0.587f*G + 0.114f*B; - p1[N] = (T)Y; - p2[N] = (T)(0.492f*(B - Y)); - p3[N] = (T)(0.877*(R - Y)); - } - return *this; - } - - //! Convert pixel values from RGB to YUV color spaces \newinstance. - CImg<Tfloat> get_RGBtoYUV() const { - return CImg<Tfloat>(*this,false).RGBtoYUV(); - } - - //! Convert pixel values from YUV to RGB color spaces. - CImg<T>& YUVtoRGB() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "YUVtoRGB(): Instance is not a YUV image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - Y = (Tfloat)p1[N], - U = (Tfloat)p2[N], - V = (Tfloat)p3[N], - R = (Y + 1.140f*V)*255, - G = (Y - 0.395f*U - 0.581f*V)*255, - B = (Y + 2.032f*U)*255; - p1[N] = (T)cimg::cut(R,0,255), - p2[N] = (T)cimg::cut(G,0,255), - p3[N] = (T)cimg::cut(B,0,255); - } - return *this; - } - - //! Convert pixel values from YUV to RGB color spaces \newinstance. - CImg<Tuchar> get_YUVtoRGB() const { - return CImg< Tuchar>(*this,false).YUVtoRGB(); - } - - //! Convert pixel values from RGB to CMY color spaces. - CImg<T>& RGBtoCMY() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoCMY(): Instance is not a RGB image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - R = (Tfloat)p1[N], - G = (Tfloat)p2[N], - B = (Tfloat)p3[N], - C = 255 - R, - M = 255 - G, - Y = 255 - B; - p1[N] = (T)cimg::cut(C,0,255), - p2[N] = (T)cimg::cut(M,0,255), - p3[N] = (T)cimg::cut(Y,0,255); - } - return *this; - } - - //! Convert pixel values from RGB to CMY color spaces \newinstance. - CImg<Tuchar> get_RGBtoCMY() const { - return CImg<Tfloat>(*this,false).RGBtoCMY(); - } - - //! Convert pixel values from CMY to RGB color spaces. - CImg<T>& CMYtoRGB() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "CMYtoRGB(): Instance is not a CMY image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - C = (Tfloat)p1[N], - M = (Tfloat)p2[N], - Y = (Tfloat)p3[N], - R = 255 - C, - G = 255 - M, - B = 255 - Y; - p1[N] = (T)cimg::cut(R,0,255), - p2[N] = (T)cimg::cut(G,0,255), - p3[N] = (T)cimg::cut(B,0,255); - } - return *this; - } - - //! Convert pixel values from CMY to RGB color spaces \newinstance. - CImg<Tuchar> get_CMYtoRGB() const { - return CImg<Tuchar>(*this,false).CMYtoRGB(); - } - - //! Convert pixel values from CMY to CMYK color spaces. - CImg<T>& CMYtoCMYK() { - return get_CMYtoCMYK().move_to(*this); - } - - //! Convert pixel values from CMY to CMYK color spaces \newinstance. - CImg<Tuchar> get_CMYtoCMYK() const { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "CMYtoCMYK(): Instance is not a CMY image.", - cimg_instance); - - CImg<Tfloat> res(_width,_height,_depth,4); - const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2); - Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) - for (ulongT N = 0; N<whd; ++N) { - Tfloat - C = (Tfloat)ps1[N], - M = (Tfloat)ps2[N], - Y = (Tfloat)ps3[N], - K = cimg::min(C,M,Y); - if (K>=255) C = M = Y = 0; - else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; } - pd1[N] = (Tfloat)cimg::cut(C,0,255), - pd2[N] = (Tfloat)cimg::cut(M,0,255), - pd3[N] = (Tfloat)cimg::cut(Y,0,255), - pd4[N] = (Tfloat)cimg::cut(K,0,255); - } - return res; - } - - //! Convert pixel values from CMYK to CMY color spaces. - CImg<T>& CMYKtoCMY() { - return get_CMYKtoCMY().move_to(*this); - } - - //! Convert pixel values from CMYK to CMY color spaces \newinstance. - CImg<Tfloat> get_CMYKtoCMY() const { - if (_spectrum!=4) - throw CImgInstanceException(_cimg_instance - "CMYKtoCMY(): Instance is not a CMYK image.", - cimg_instance); - - CImg<Tfloat> res(_width,_height,_depth,3); - const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3); - Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - C = (Tfloat)ps1[N], - M = (Tfloat)ps2[N], - Y = (Tfloat)ps3[N], - K = (Tfloat)ps4[N], - K1 = 1 - K/255, - nC = C*K1 + K, - nM = M*K1 + K, - nY = Y*K1 + K; - pd1[N] = (Tfloat)cimg::cut(nC,0,255), - pd2[N] = (Tfloat)cimg::cut(nM,0,255), - pd3[N] = (Tfloat)cimg::cut(nY,0,255); - } - return res; - } - - //! Convert pixel values from RGB to XYZ color spaces. - /** - \param use_D65 Tell to use the D65 illuminant (D50 otherwise). - **/ - CImg<T>& RGBtoXYZ(const bool use_D65=true) { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoXYZ(): Instance is not a RGB image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - R = (Tfloat)p1[N]/255, - G = (Tfloat)p2[N]/255, - B = (Tfloat)p3[N]/255; - if (use_D65) { // D65 - p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B); - p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B); - p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B); - } else { // D50 - p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B); - p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B); - p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B); - } - } - return *this; - } - - //! Convert pixel values from RGB to XYZ color spaces \newinstance. - CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const { - return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65); - } - - //! Convert pixel values from XYZ to RGB color spaces. - /** - \param use_D65 Tell to use the D65 illuminant (D50 otherwise). - **/ - CImg<T>& XYZtoRGB(const bool use_D65=true) { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "XYZtoRGB(): Instance is not a XYZ image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - X = (Tfloat)p1[N]*255, - Y = (Tfloat)p2[N]*255, - Z = (Tfloat)p3[N]*255; - if (use_D65) { - p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255); - p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255); - p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255); - } else { - p1[N] = (T)cimg::cut(3.134274799724*X - 1.617275708956*Y - 0.490724283042*Z,0,255); - p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255); - p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255); - } - } - return *this; - } - - //! Convert pixel values from XYZ to RGB color spaces \newinstance. - CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const { - return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65); - } - - //! Convert pixel values from XYZ to Lab color spaces. - CImg<T>& XYZtoLab(const bool use_D65=true) { -#define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116) - - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "XYZtoLab(): Instance is not a XYZ image.", - cimg_instance); - const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65); - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - X = (Tfloat)(p1[N]/white[0]), - Y = (Tfloat)(p2[N]/white[1]), - Z = (Tfloat)(p3[N]/white[2]), - fX = (Tfloat)_cimg_Labf(X), - fY = (Tfloat)_cimg_Labf(Y), - fZ = (Tfloat)_cimg_Labf(Z); - p1[N] = (T)cimg::cut(116*fY - 16,0,100); - p2[N] = (T)(500*(fX - fY)); - p3[N] = (T)(200*(fY - fZ)); - } - return *this; - } - - //! Convert pixel values from XYZ to Lab color spaces \newinstance. - CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const { - return CImg<Tfloat>(*this,false).XYZtoLab(use_D65); - } - - //! Convert pixel values from Lab to XYZ color spaces. - CImg<T>& LabtoXYZ(const bool use_D65=true) { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "LabtoXYZ(): Instance is not a Lab image.", - cimg_instance); - const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65); - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - L = (Tfloat)p1[N], - a = (Tfloat)p2[N], - b = (Tfloat)p3[N], - cY = (L + 16)/116, - cZ = cY - b/200, - cX = a/500 + cY, - X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389), - Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389), - Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389); - p1[N] = (T)(X*white[0]); - p2[N] = (T)(Y*white[1]); - p3[N] = (T)(Z*white[2]); - } - return *this; - } - - //! Convert pixel values from Lab to XYZ color spaces \newinstance. - CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const { - return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65); - } - - //! Convert pixel values from XYZ to xyY color spaces. - CImg<T>& XYZtoxyY() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "XYZtoxyY(): Instance is not a XYZ image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - X = (Tfloat)p1[N], - Y = (Tfloat)p2[N], - Z = (Tfloat)p3[N], - sum = X + Y + Z, - nsum = sum>0?sum:1; - p1[N] = (T)(X/nsum); - p2[N] = (T)(Y/nsum); - p3[N] = (T)Y; - } - return *this; - } - - //! Convert pixel values from XYZ to xyY color spaces \newinstance. - CImg<Tfloat> get_XYZtoxyY() const { - return CImg<Tfloat>(*this,false).XYZtoxyY(); - } - - //! Convert pixel values from xyY pixels to XYZ color spaces. - CImg<T>& xyYtoXYZ() { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "xyYtoXYZ(): Instance is not a xyY image.", - cimg_instance); - - T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) - for (ulongT N = 0; N<whd; ++N) { - const Tfloat - px = (Tfloat)p1[N], - py = (Tfloat)p2[N], - Y = (Tfloat)p3[N], - ny = py>0?py:1; - p1[N] = (T)(px*Y/ny); - p2[N] = (T)Y; - p3[N] = (T)((1 - px - py)*Y/ny); - } - return *this; - } - - //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance. - CImg<Tfloat> get_xyYtoXYZ() const { - return CImg<Tfloat>(*this,false).xyYtoXYZ(); - } - - //! Convert pixel values from RGB to Lab color spaces. - CImg<T>& RGBtoLab(const bool use_D65=true) { - return RGBtoXYZ(use_D65).XYZtoLab(use_D65); - } - - //! Convert pixel values from RGB to Lab color spaces \newinstance. - CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const { - return CImg<Tfloat>(*this,false).RGBtoLab(use_D65); - } - - //! Convert pixel values from Lab to RGB color spaces. - CImg<T>& LabtoRGB(const bool use_D65=true) { - return LabtoXYZ().XYZtoRGB(use_D65); - } - - //! Convert pixel values from Lab to RGB color spaces \newinstance. - CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const { - return CImg<Tuchar>(*this,false).LabtoRGB(use_D65); - } - - //! Convert pixel values from RGB to xyY color spaces. - CImg<T>& RGBtoxyY(const bool use_D65=true) { - return RGBtoXYZ(use_D65).XYZtoxyY(); - } - - //! Convert pixel values from RGB to xyY color spaces \newinstance. - CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const { - return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65); - } - - //! Convert pixel values from xyY to RGB color spaces. - CImg<T>& xyYtoRGB(const bool use_D65=true) { - return xyYtoXYZ().XYZtoRGB(use_D65); - } - - //! Convert pixel values from xyY to RGB color spaces \newinstance. - CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const { - return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65); - } - - //! Convert pixel values from RGB to CMYK color spaces. - CImg<T>& RGBtoCMYK() { - return RGBtoCMY().CMYtoCMYK(); - } - - //! Convert pixel values from RGB to CMYK color spaces \newinstance. - CImg<Tfloat> get_RGBtoCMYK() const { - return CImg<Tfloat>(*this,false).RGBtoCMYK(); - } - - //! Convert pixel values from CMYK to RGB color spaces. - CImg<T>& CMYKtoRGB() { - return CMYKtoCMY().CMYtoRGB(); - } - - //! Convert pixel values from CMYK to RGB color spaces \newinstance. - CImg<Tuchar> get_CMYKtoRGB() const { - return CImg<Tuchar>(*this,false).CMYKtoRGB(); - } - - //@} - //------------------------------------------ - // - //! \name Geometric / Spatial Manipulation - //@{ - //------------------------------------------ - - static float _cimg_lanczos(const float x) { - if (x<=-2 || x>=2) return 0; - const float a = (float)cimg::PI*x, b = 0.5f*a; - return (float)(x?std::sin(a)*std::sin(b)/(a*b):1); - } - - //! Resize image to new dimensions. - /** - \param size_x Number of columns (new size along the X-axis). - \param size_y Number of rows (new size along the Y-axis). - \param size_z Number of slices (new size along the Z-axis). - \param size_c Number of vector-channels (new size along the C-axis). - \param interpolation_type Method of interpolation: - - -1 = no interpolation: raw memory resizing. - - 0 = no interpolation: additional space is filled according to \p boundary_conditions. - - 1 = nearest-neighbor interpolation. - - 2 = moving average interpolation. - - 3 = linear interpolation. - - 4 = grid interpolation. - - 5 = cubic interpolation. - - 6 = lanczos interpolation. - \param boundary_conditions Type of boundary conditions used if necessary. - \param centering_x Set centering type (only if \p interpolation_type=0). - \param centering_y Set centering type (only if \p interpolation_type=0). - \param centering_z Set centering type (only if \p interpolation_type=0). - \param centering_c Set centering type (only if \p interpolation_type=0). - \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). - **/ - CImg<T>& resize(const int size_x, const int size_y=-100, - const int size_z=-100, const int size_c=-100, - const int interpolation_type=1, const unsigned int boundary_conditions=0, - const float centering_x = 0, const float centering_y = 0, - const float centering_z = 0, const float centering_c = 0) { - if (!size_x || !size_y || !size_z || !size_c) return assign(); - const unsigned int - _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x), - _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y), - _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z), - _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c), - sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1; - if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this; - if (is_empty()) return assign(sx,sy,sz,sc,(T)0); - if (interpolation_type==-1 && sx*sy*sz*sc==size()) { - _width = sx; _height = sy; _depth = sz; _spectrum = sc; - return *this; - } - return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions, - centering_x,centering_y,centering_z,centering_c).move_to(*this); - } - - //! Resize image to new dimensions \newinstance. - CImg<T> get_resize(const int size_x, const int size_y = -100, - const int size_z = -100, const int size_c = -100, - const int interpolation_type=1, const unsigned int boundary_conditions=0, - const float centering_x = 0, const float centering_y = 0, - const float centering_z = 0, const float centering_c = 0) const { - if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 || - centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1) - throw CImgArgumentException(_cimg_instance - "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].", - cimg_instance, - centering_x,centering_y,centering_z,centering_c); - - if (!size_x || !size_y || !size_z || !size_c) return CImg<T>(); - const unsigned int - sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)), - sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)), - sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)), - sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100)); - if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this; - if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0); - CImg<T> res; - switch (interpolation_type) { - - // Raw resizing. - // - case -1 : - std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc)); - break; - - // No interpolation. - // - case 0 : { - const int - xc = (int)(centering_x*((int)sx - width())), - yc = (int)(centering_y*((int)sy - height())), - zc = (int)(centering_z*((int)sz - depth())), - cc = (int)(centering_c*((int)sc - spectrum())); - - switch (boundary_conditions) { - case 3 : { // Mirror - res.assign(sx,sy,sz,sc); - const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),65536)) - cimg_forXYZC(res,x,y,z,c) { - const int - mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2), - mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2); - res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1, - mc<spectrum()?mc:s2 - mc - 1); - } - } break; - case 2 : { // Periodic - res.assign(sx,sy,sz,sc); - const int - x0 = ((int)xc%width()) - width(), - y0 = ((int)yc%height()) - height(), - z0 = ((int)zc%depth()) - depth(), - c0 = ((int)cc%spectrum()) - spectrum(), - dx = width(), dy = height(), dz = depth(), dc = spectrum(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),65536)) - for (int c = c0; c<(int)sc; c+=dc) - for (int z = z0; z<(int)sz; z+=dz) - for (int y = y0; y<(int)sy; y+=dy) - for (int x = x0; x<(int)sx; x+=dx) - res.draw_image(x,y,z,c,*this); - } break; - case 1 : { // Neumann - res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this); - CImg<T> sprite; - if (xc>0) { // X-backward - res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); - for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite); - } - if (xc + width()<(int)sx) { // X-forward - res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1, - zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); - for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite); - } - if (yc>0) { // Y-backward - res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); - for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite); - } - if (yc + height()<(int)sy) { // Y-forward - res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1, - zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); - for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite); - } - if (zc>0) { // Z-backward - res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite); - for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite); - } - if (zc + depth()<(int)sz) { // Z-forward - res.get_crop(0,0,zc +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); - for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite); - } - if (cc>0) { // C-backward - res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite); - for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite); - } - if (cc + spectrum()<(int)sc) { // C-forward - res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite); - for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite); - } - } break; - default : // Dirichlet - res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this); - } - break; - } break; - - // Nearest neighbor interpolation. - // - case 1 : { - res.assign(sx,sy,sz,sc); - CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1); - const ulongT - wh = (ulongT)_width*_height, - whd = (ulongT)_width*_height*_depth, - sxy = (ulongT)sx*sy, - sxyz = (ulongT)sx*sy*sz, - one = (ulongT)1; - if (sx==_width) off_x.fill(1); - else { - ulongT *poff_x = off_x._data, curr = 0; - cimg_forX(res,x) { - const ulongT old = curr; - curr = (x + one)*_width/sx; - *(poff_x++) = curr - old; - } - } - if (sy==_height) off_y.fill(_width); - else { - ulongT *poff_y = off_y._data, curr = 0; - cimg_forY(res,y) { - const ulongT old = curr; - curr = (y + one)*_height/sy; - *(poff_y++) = _width*(curr - old); - } - *poff_y = 0; - } - if (sz==_depth) off_z.fill(wh); - else { - ulongT *poff_z = off_z._data, curr = 0; - cimg_forZ(res,z) { - const ulongT old = curr; - curr = (z + one)*_depth/sz; - *(poff_z++) = wh*(curr - old); - } - *poff_z = 0; - } - if (sc==_spectrum) off_c.fill(whd); - else { - ulongT *poff_c = off_c._data, curr = 0; - cimg_forC(res,c) { - const ulongT old = curr; - curr = (c + one)*_spectrum/sc; - *(poff_c++) = whd*(curr - old); - } - *poff_c = 0; - } - - T *ptrd = res._data; - const T* ptrc = _data; - const ulongT *poff_c = off_c._data; - for (unsigned int c = 0; c<sc; ) { - const T *ptrz = ptrc; - const ulongT *poff_z = off_z._data; - for (unsigned int z = 0; z<sz; ) { - const T *ptry = ptrz; - const ulongT *poff_y = off_y._data; - for (unsigned int y = 0; y<sy; ) { - const T *ptrx = ptry; - const ulongT *poff_x = off_x._data; - cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); } - ++y; - ulongT dy = *(poff_y++); - for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {} - ptry+=dy; - } - ++z; - ulongT dz = *(poff_z++); - for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd - sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {} - ptrz+=dz; - } - ++c; - ulongT dc = *(poff_c++); - for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd - sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {} - ptrc+=dc; - } - } break; - - // Moving average. - // - case 2 : { - bool instance_first = true; - if (sx!=_width) { - CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0); - for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; - if (!b) { - cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width; - ++t; - b = _width; - } - if (!c) { ++s; c = sx; } - } - tmp.move_to(res); - instance_first = false; - } - if (sy!=_height) { - CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0); - for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - if (instance_first) - cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; - else - cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; - if (!b) { - cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height; - ++t; - b = _height; - } - if (!c) { ++s; c = sy; } - } - tmp.move_to(res); - instance_first = false; - } - if (sz!=_depth) { - CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0); - for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - if (instance_first) - cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; - else - cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; - if (!b) { - cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth; - ++t; - b = _depth; - } - if (!c) { ++s; c = sz; } - } - tmp.move_to(res); - instance_first = false; - } - if (sc!=_spectrum) { - CImg<Tfloat> tmp(sx,sy,sz,sc,0); - for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - if (instance_first) - cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; - else - cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; - if (!b) { - cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum; - ++t; - b = _spectrum; - } - if (!c) { ++s; c = sc; } - } - tmp.move_to(res); - instance_first = false; - } - } break; - - // Linear interpolation. - // - case 3 : { - CImg<uintT> off(cimg::max(sx,sy,sz,sc)); - CImg<doubleT> foff(off._width); - CImg<T> resx, resy, resz, resc; - double curr, old; - - if (sx!=_width) { - if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); - else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); - else { - const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): - (double)_width/sx; - resx.assign(sx,_height,_depth,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forX(resx,x) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(width() - 1.,curr + fx); - *(poff++) = (unsigned int)curr - (unsigned int)old; - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resx.size(),65536)) - cimg_forYZC(resx,y,z,c) { - const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1; - T *ptrd = resx.data(0,y,z,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forX(resx,x) { - const double alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1; - *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2); - ptrs+=*(poff++); - } - } - } - } else resx.assign(*this,true); - - if (sy!=_height) { - if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); - else { - if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); - else { - const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): - (double)_height/sy; - resy.assign(sx,sy,_depth,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forY(resy,y) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(height() - 1.,curr + fy); - *(poff++) = sx*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resy.size(),65536)) - cimg_forXZC(resy,x,z,c) { - const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx; - T *ptrd = resy.data(x,0,z,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forY(resy,y) { - const double alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1; - *ptrd = (T)((1 - alpha)*val1 + alpha*val2); - ptrd+=sx; - ptrs+=*(poff++); - } - } - } - } - resx.assign(); - } else resy.assign(resx,true); - - if (sz!=_depth) { - if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); - else { - if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); - else { - const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): - (double)_depth/sz; - const unsigned int sxy = sx*sy; - resz.assign(sx,sy,sz,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forZ(resz,z) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(depth() - 1.,curr + fz); - *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resz.size(),65536)) - cimg_forXYC(resz,x,y,c) { - const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy; - T *ptrd = resz.data(x,y,0,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forZ(resz,z) { - const double alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1; - *ptrd = (T)((1 - alpha)*val1 + alpha*val2); - ptrd+=sxy; - ptrs+=*(poff++); - } - } - } - } - resy.assign(); - } else resz.assign(resy,true); - - if (sc!=_spectrum) { - if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); - else { - if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); - else { - const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): - (double)_spectrum/sc; - const unsigned int sxyz = sx*sy*sz; - resc.assign(sx,sy,sz,sc); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forC(resc,c) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(spectrum() - 1.,curr + fc); - *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resc.size(),65536)) - cimg_forXYZ(resc,x,y,z) { - const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz; - T *ptrd = resc.data(x,y,z,0); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forC(resc,c) { - const double alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1; - *ptrd = (T)((1 - alpha)*val1 + alpha*val2); - ptrd+=sxyz; - ptrs+=*(poff++); - } - } - } - } - resz.assign(); - } else resc.assign(resz,true); - return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; - } break; - - // Grid interpolation. - // - case 4 : { - CImg<T> resx, resy, resz, resc; - if (sx!=_width) { - if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); - else { - resx.assign(sx,_height,_depth,_spectrum,(T)0); - const int dx = (int)(2*sx), dy = 2*width(); - int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0; - cimg_forX(resx,x) if ((err-=dy)<=0) { - cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c); - ++xs; - err+=dx; - } - } - } else resx.assign(*this,true); - - if (sy!=_height) { - if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); - else { - resy.assign(sx,sy,_depth,_spectrum,(T)0); - const int dx = (int)(2*sy), dy = 2*height(); - int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0; - cimg_forY(resy,y) if ((err-=dy)<=0) { - cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c); - ++ys; - err+=dx; - } - } - resx.assign(); - } else resy.assign(resx,true); - - if (sz!=_depth) { - if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); - else { - resz.assign(sx,sy,sz,_spectrum,(T)0); - const int dx = (int)(2*sz), dy = 2*depth(); - int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0; - cimg_forZ(resz,z) if ((err-=dy)<=0) { - cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c); - ++zs; - err+=dx; - } - } - resy.assign(); - } else resz.assign(resy,true); - - if (sc!=_spectrum) { - if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); - else { - resc.assign(sx,sy,sz,sc,(T)0); - const int dx = (int)(2*sc), dy = 2*spectrum(); - int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0; - cimg_forC(resc,c) if ((err-=dy)<=0) { - cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs); - ++cs; - err+=dx; - } - } - resz.assign(); - } else resc.assign(resz,true); - - return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; - } break; - - // Cubic interpolation. - // - case 5 : { - const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max(); - CImg<uintT> off(cimg::max(sx,sy,sz,sc)); - CImg<doubleT> foff(off._width); - CImg<T> resx, resy, resz, resc; - double curr, old; - - if (sx!=_width) { - if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); - else { - if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); - else { - const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): - (double)_width/sx; - resx.assign(sx,_height,_depth,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forX(resx,x) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(width() - 1.,curr + fx); - *(poff++) = (unsigned int)curr - (unsigned int)old; - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resx.size(),65536)) - cimg_forYZC(resx,y,z,c) { - const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2); - T *ptrd = resx.data(0,y,z,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forX(resx,x) { - const double - t = *(pfoff++), - val1 = (double)*ptrs, - val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1, - val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1, - val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2, - val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) + - t*t*t*(-val0 + 3*val1 - 3*val2 + val3)); - *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrs+=*(poff++); - } - } - } - } - } else resx.assign(*this,true); - - if (sy!=_height) { - if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); - else { - if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); - else { - const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): - (double)_height/sy; - resy.assign(sx,sy,_depth,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forY(resy,y) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(height() - 1.,curr + fy); - *(poff++) = sx*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resy.size(),65536)) - cimg_forXZC(resy,x,z,c) { - const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx; - T *ptrd = resy.data(x,0,z,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forY(resy,y) { - const double - t = *(pfoff++), - val1 = (double)*ptrs, - val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1, - val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1, - val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2, - val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) + - t*t*t*(-val0 + 3*val1 - 3*val2 + val3)); - *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrd+=sx; - ptrs+=*(poff++); - } - } - } - } - resx.assign(); - } else resy.assign(resx,true); - - if (sz!=_depth) { - if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); - else { - if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); - else { - const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): - (double)_depth/sz; - const unsigned int sxy = sx*sy; - resz.assign(sx,sy,sz,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forZ(resz,z) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(depth() - 1.,curr + fz); - *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resz.size(),65536)) - cimg_forXYC(resz,x,y,c) { - const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy; - T *ptrd = resz.data(x,y,0,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forZ(resz,z) { - const double - t = *(pfoff++), - val1 = (double)*ptrs, - val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1, - val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1, - val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2, - val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) + - t*t*t*(-val0 + 3*val1 - 3*val2 + val3)); - *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrd+=sxy; - ptrs+=*(poff++); - } - } - } - } - resy.assign(); - } else resz.assign(resy,true); - - if (sc!=_spectrum) { - if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); - else { - if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); - else { - const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): - (double)_spectrum/sc; - const unsigned int sxyz = sx*sy*sz; - resc.assign(sx,sy,sz,sc); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forC(resc,c) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(spectrum() - 1.,curr + fc); - *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resc.size(),65536)) - cimg_forXYZ(resc,x,y,z) { - const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; - T *ptrd = resc.data(x,y,z,0); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forC(resc,c) { - const double - t = *(pfoff++), - val1 = (double)*ptrs, - val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1, - val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1, - val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2, - val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) + - t*t*t*(-val0 + 3*val1 - 3*val2 + val3)); - *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrd+=sxyz; - ptrs+=*(poff++); - } - } - } - } - resz.assign(); - } else resc.assign(resz,true); - - return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; - } break; - - // Lanczos interpolation. - // - case 6 : { - const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max(); - CImg<uintT> off(cimg::max(sx,sy,sz,sc)); - CImg<doubleT> foff(off._width); - CImg<T> resx, resy, resz, resc; - double curr, old; - - if (sx!=_width) { - if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); - else { - if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); - else { - const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): - (double)_width/sx; - resx.assign(sx,_height,_depth,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forX(resx,x) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(width() - 1.,curr + fx); - *(poff++) = (unsigned int)curr - (unsigned int)old; - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resx.size(),65536)) - cimg_forYZC(resx,y,z,c) { - const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, - *const ptrsmax = ptrs0 + (_width - 2); - T *ptrd = resx.data(0,y,z,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forX(resx,x) { - const double - t = *(pfoff++), - w0 = _cimg_lanczos(t + 2), - w1 = _cimg_lanczos(t + 1), - w2 = _cimg_lanczos(t), - w3 = _cimg_lanczos(t - 1), - w4 = _cimg_lanczos(t - 2), - val2 = (double)*ptrs, - val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2, - val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1, - val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2, - val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3, - val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4); - *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrs+=*(poff++); - } - } - } - } - } else resx.assign(*this,true); - - if (sy!=_height) { - if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); - else { - if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); - else { - const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): - (double)_height/sy; - resy.assign(sx,sy,_depth,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forY(resy,y) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(height() - 1.,curr + fy); - *(poff++) = sx*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resy.size(),65536)) - cimg_forXZC(resy,x,z,c) { - const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, - *const ptrsmax = ptrs0 + (_height - 2)*sx; - T *ptrd = resy.data(x,0,z,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forY(resy,y) { - const double - t = *(pfoff++), - w0 = _cimg_lanczos(t + 2), - w1 = _cimg_lanczos(t + 1), - w2 = _cimg_lanczos(t), - w3 = _cimg_lanczos(t - 1), - w4 = _cimg_lanczos(t - 2), - val2 = (double)*ptrs, - val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2, - val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1, - val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2, - val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3, - val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4); - *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrd+=sx; - ptrs+=*(poff++); - } - } - } - } - resx.assign(); - } else resy.assign(resx,true); - - if (sz!=_depth) { - if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); - else { - if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); - else { - const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): - (double)_depth/sz; - const unsigned int sxy = sx*sy; - resz.assign(sx,sy,sz,_spectrum); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forZ(resz,z) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(depth() - 1.,curr + fz); - *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resz.size(),65536)) - cimg_forXYC(resz,x,y,c) { - const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, - *const ptrsmax = ptrs0 + (_depth - 2)*sxy; - T *ptrd = resz.data(x,y,0,c); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forZ(resz,z) { - const double - t = *(pfoff++), - w0 = _cimg_lanczos(t + 2), - w1 = _cimg_lanczos(t + 1), - w2 = _cimg_lanczos(t), - w3 = _cimg_lanczos(t - 1), - w4 = _cimg_lanczos(t - 2), - val2 = (double)*ptrs, - val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2, - val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1, - val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2, - val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3, - val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4); - *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrd+=sxy; - ptrs+=*(poff++); - } - } - } - } - resy.assign(); - } else resz.assign(resy,true); - - if (sc!=_spectrum) { - if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); - else { - if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); - else { - const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): - (double)_spectrum/sc; - const unsigned int sxyz = sx*sy*sz; - resc.assign(sx,sy,sz,sc); - curr = old = 0; - unsigned int *poff = off._data; - double *pfoff = foff._data; - cimg_forC(resc,c) { - *(pfoff++) = curr - (unsigned int)curr; - old = curr; - curr = std::min(spectrum() - 1.,curr + fc); - *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); - } - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(resc.size(),65536)) - cimg_forXYZ(resc,x,y,z) { - const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, - *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; - T *ptrd = resc.data(x,y,z,0); - const unsigned int *poff = off._data; - const double *pfoff = foff._data; - cimg_forC(resc,c) { - const double - t = *(pfoff++), - w0 = _cimg_lanczos(t + 2), - w1 = _cimg_lanczos(t + 1), - w2 = _cimg_lanczos(t), - w3 = _cimg_lanczos(t - 1), - w4 = _cimg_lanczos(t - 2), - val2 = (double)*ptrs, - val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2, - val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1, - val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2, - val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3, - val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4); - *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val); - ptrd+=sxyz; - ptrs+=*(poff++); - } - } - } - } - resz.assign(); - } else resc.assign(resz,true); - - return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; - } break; - - // Unknow interpolation. - // - default : - throw CImgArgumentException(_cimg_instance - "resize(): Invalid specified interpolation %d " - "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | " - "5=cubic | 6=lanczos }).", - cimg_instance, - interpolation_type); - } - return res; - } - - //! Resize image to dimensions of another image. - /** - \param src Reference image used for dimensions. - \param interpolation_type Interpolation method. - \param boundary_conditions Boundary conditions. - \param centering_x Set centering type (only if \p interpolation_type=0). - \param centering_y Set centering type (only if \p interpolation_type=0). - \param centering_z Set centering type (only if \p interpolation_type=0). - \param centering_c Set centering type (only if \p interpolation_type=0). - **/ - template<typename t> - CImg<T>& resize(const CImg<t>& src, - const int interpolation_type=1, const unsigned int boundary_conditions=0, - const float centering_x = 0, const float centering_y = 0, - const float centering_z = 0, const float centering_c = 0) { - return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, - centering_x,centering_y,centering_z,centering_c); - } - - //! Resize image to dimensions of another image \newinstance. - template<typename t> - CImg<T> get_resize(const CImg<t>& src, - const int interpolation_type=1, const unsigned int boundary_conditions=0, - const float centering_x = 0, const float centering_y = 0, - const float centering_z = 0, const float centering_c = 0) const { - return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, - centering_x,centering_y,centering_z,centering_c); - } - - //! Resize image to dimensions of a display window. - /** - \param disp Reference display window used for dimensions. - \param interpolation_type Interpolation method. - \param boundary_conditions Boundary conditions. - \param centering_x Set centering type (only if \p interpolation_type=0). - \param centering_y Set centering type (only if \p interpolation_type=0). - \param centering_z Set centering type (only if \p interpolation_type=0). - \param centering_c Set centering type (only if \p interpolation_type=0). - **/ - CImg<T>& resize(const CImgDisplay& disp, - const int interpolation_type=1, const unsigned int boundary_conditions=0, - const float centering_x = 0, const float centering_y = 0, - const float centering_z = 0, const float centering_c = 0) { - return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, - centering_x,centering_y,centering_z,centering_c); - } - - //! Resize image to dimensions of a display window \newinstance. - CImg<T> get_resize(const CImgDisplay& disp, - const int interpolation_type=1, const unsigned int boundary_conditions=0, - const float centering_x = 0, const float centering_y = 0, - const float centering_z = 0, const float centering_c = 0) const { - return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, - centering_x,centering_y,centering_z,centering_c); - } - - //! Resize image to half-size along XY axes, using an optimized filter. - CImg<T>& resize_halfXY() { - return get_resize_halfXY().move_to(*this); - } - - //! Resize image to half-size along XY axes, using an optimized filter \newinstance. - CImg<T> get_resize_halfXY() const { - if (is_empty()) return *this; - static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, - 0.1231940459f, 0.1935127547f, 0.1231940459f, - 0.07842776544f, 0.1231940459f, 0.07842776544f }; - CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum); - T *ptrd = res._data; - cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) - if (x%2 && y%2) *(ptrd++) = (T) - (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] + - I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] + - I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]); - return res; - } - - //! Resize image to double-size, using the Scale2X algorithm. - /** - \note Use anisotropic upscaling algorithm - <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>. - **/ - CImg<T>& resize_doubleXY() { - return get_resize_doubleXY().move_to(*this); - } - - //! Resize image to double-size, using the Scale2X algorithm \newinstance. - CImg<T> get_resize_doubleXY() const { -#define _cimg_gs2x_for3(bound,i) \ - for (int i = 0, _p1##i = 0, \ - _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ - _n1##i<(int)(bound) || i==--_n1##i; \ - _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width) - -#define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \ - _cimg_gs2x_for3((img)._height,y) for (int x = 0, \ - _p1##x = 0, \ - _n1##x = (int)( \ - (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[3] = I[4] = (T)(img)(0,y,z,c)), \ - (I[7] = (T)(img)(0,_n1##y,z,c)), \ - 1>=(img)._width?(img).width() - 1:1); \ - (_n1##x<(img).width() && ( \ - (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,y,z,c)), \ - (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ - x==--_n1##x; \ - I[1] = I[2], \ - I[3] = I[4], I[4] = I[5], \ - I[7] = I[8], \ - _p1##x = x++, ++_n1##x) - - if (is_empty()) return *this; - CImg<T> res(_width<<1,_height<<1,_depth,_spectrum); - CImg_3x3(I,T); - cimg_forZC(*this,z,c) { - T - *ptrd1 = res.data(0,0,z,c), - *ptrd2 = ptrd1 + res._width; - _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) { - if (Icp!=Icn && Ipc!=Inc) { - *(ptrd1++) = Ipc==Icp?Ipc:Icc; - *(ptrd1++) = Icp==Inc?Inc:Icc; - *(ptrd2++) = Ipc==Icn?Ipc:Icc; - *(ptrd2++) = Icn==Inc?Inc:Icc; - } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; } - } - } - return res; - } - - //! Resize image to triple-size, using the Scale3X algorithm. - /** - \note Use anisotropic upscaling algorithm - <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>. - **/ - CImg<T>& resize_tripleXY() { - return get_resize_tripleXY().move_to(*this); - } - - //! Resize image to triple-size, using the Scale3X algorithm \newinstance. - CImg<T> get_resize_tripleXY() const { -#define _cimg_gs3x_for3(bound,i) \ - for (int i = 0, _p1##i = 0, \ - _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ - _n1##i<(int)(bound) || i==--_n1##i; \ - _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width) - -#define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \ - _cimg_gs3x_for3((img)._height,y) for (int x = 0, \ - _p1##x = 0, \ - _n1##x = (int)( \ - (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ - (I[3] = I[4] = (T)(img)(0,y,z,c)), \ - (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ - 1>=(img)._width?(img).width() - 1:1); \ - (_n1##x<(img).width() && ( \ - (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ - (I[5] = (T)(img)(_n1##x,y,z,c)), \ - (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ - x==--_n1##x; \ - I[0] = I[1], I[1] = I[2], \ - I[3] = I[4], I[4] = I[5], \ - I[6] = I[7], I[7] = I[8], \ - _p1##x = x++, ++_n1##x) - - if (is_empty()) return *this; - CImg<T> res(3*_width,3*_height,_depth,_spectrum); - CImg_3x3(I,T); - cimg_forZC(*this,z,c) { - T - *ptrd1 = res.data(0,0,z,c), - *ptrd2 = ptrd1 + res._width, - *ptrd3 = ptrd2 + res._width; - _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) { - if (Icp != Icn && Ipc != Inc) { - *(ptrd1++) = Ipc==Icp?Ipc:Icc; - *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc; - *(ptrd1++) = Icp==Inc?Inc:Icc; - *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc; - *(ptrd2++) = Icc; - *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc; - *(ptrd3++) = Ipc==Icn?Ipc:Icc; - *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc; - *(ptrd3++) = Icn==Inc?Inc:Icc; - } else { - *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc; - *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; - *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc; - } - } - } - return res; - } - - //! Mirror image content along specified axis. - /** - \param axis Mirror axis - **/ - CImg<T>& mirror(const char axis) { - if (is_empty()) return *this; - T *pf, *pb, *buf = 0; - switch (cimg::lowercase(axis)) { - case 'x' : { - pf = _data; pb = data(_width - 1); - const unsigned int width2 = _width/2; - for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) { - for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; } - pf+=_width - width2; - pb+=_width + width2; - } - } break; - case 'y' : { - buf = new T[_width]; - pf = _data; pb = data(0,_height - 1); - const unsigned int height2 = _height/2; - for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) { - for (unsigned int y = 0; y<height2; ++y) { - std::memcpy(buf,pf,_width*sizeof(T)); - std::memcpy(pf,pb,_width*sizeof(T)); - std::memcpy(pb,buf,_width*sizeof(T)); - pf+=_width; - pb-=_width; - } - pf+=(ulongT)_width*(_height - height2); - pb+=(ulongT)_width*(_height + height2); - } - } break; - case 'z' : { - buf = new T[(ulongT)_width*_height]; - pf = _data; pb = data(0,0,_depth - 1); - const unsigned int depth2 = _depth/2; - cimg_forC(*this,c) { - for (unsigned int z = 0; z<depth2; ++z) { - std::memcpy(buf,pf,_width*_height*sizeof(T)); - std::memcpy(pf,pb,_width*_height*sizeof(T)); - std::memcpy(pb,buf,_width*_height*sizeof(T)); - pf+=(ulongT)_width*_height; - pb-=(ulongT)_width*_height; - } - pf+=(ulongT)_width*_height*(_depth - depth2); - pb+=(ulongT)_width*_height*(_depth + depth2); - } - } break; - case 'c' : { - buf = new T[(ulongT)_width*_height*_depth]; - pf = _data; pb = data(0,0,0,_spectrum - 1); - const unsigned int _spectrum2 = _spectrum/2; - for (unsigned int v = 0; v<_spectrum2; ++v) { - std::memcpy(buf,pf,_width*_height*_depth*sizeof(T)); - std::memcpy(pf,pb,_width*_height*_depth*sizeof(T)); - std::memcpy(pb,buf,_width*_height*_depth*sizeof(T)); - pf+=(ulongT)_width*_height*_depth; - pb-=(ulongT)_width*_height*_depth; - } - } break; - default : - throw CImgArgumentException(_cimg_instance - "mirror(): Invalid specified axis '%c'.", - cimg_instance, - axis); - } - delete[] buf; - return *this; - } - - //! Mirror image content along specified axis \newinstance. - CImg<T> get_mirror(const char axis) const { - return (+*this).mirror(axis); - } - - //! Mirror image content along specified axes. - /** - \param axes Mirror axes, as a C-string. - \note \c axes may contains multiple characters, e.g. \c "xyz" - **/ - CImg<T>& mirror(const char *const axes) { - for (const char *s = axes; *s; ++s) mirror(*s); - return *this; - } - - //! Mirror image content along specified axes \newinstance. - CImg<T> get_mirror(const char *const axes) const { - return (+*this).mirror(axes); - } - - //! Shift image content. - /** - \param delta_x Amount of displacement along the X-axis. - \param delta_y Amount of displacement along the Y-axis. - \param delta_z Amount of displacement along the Z-axis. - \param delta_c Amount of displacement along the C-axis. - \param boundary_conditions Border condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. - **/ - CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, - const unsigned int boundary_conditions=0) { - if (is_empty()) return *this; - if (boundary_conditions==3) - return get_crop(-delta_x,-delta_y,-delta_z,-delta_c, - width() - delta_x - 1, - height() - delta_y - 1, - depth() - delta_z - 1, - spectrum() - delta_c - 1,3).move_to(*this); - if (delta_x) // Shift along X-axis - switch (boundary_conditions) { - case 2 : { // Periodic - const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width()); - if (!ndelta_x) return *this; - CImg<T> buf(cimg::abs(ndelta_x)); - if (ndelta_x>0) cimg_forYZC(*this,y,z,c) { - std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T)); - std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); - std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T)); - } else cimg_forYZC(*this,y,z,c) { - std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T)); - std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T)); - std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T)); - } - } break; - case 1 : // Neumann - if (delta_x<0) { - const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x; - if (!ndelta_x) return *this; - cimg_forYZC(*this,y,z,c) { - std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); - T *ptrd = data(_width - 1,y,z,c); - const T val = *ptrd; - for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val; - } - } else { - const int ndelta_x = (delta_x>=width())?width() - 1:delta_x; - if (!ndelta_x) return *this; - cimg_forYZC(*this,y,z,c) { - std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T)); - T *ptrd = data(0,y,z,c); - const T val = *ptrd; - for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val; - } - } - break; - default : // Dirichlet - if (delta_x<=-width() || delta_x>=width()) return fill((T)0); - if (delta_x<0) cimg_forYZC(*this,y,z,c) { - std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T)); - std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T)); - } else cimg_forYZC(*this,y,z,c) { - std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T)); - std::memset(data(0,y,z,c),0,delta_x*sizeof(T)); - } - } - - if (delta_y) // Shift along Y-axis - switch (boundary_conditions) { - case 2 : { // Periodic - const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height()); - if (!ndelta_y) return *this; - CImg<T> buf(width(),cimg::abs(ndelta_y)); - if (ndelta_y>0) cimg_forZC(*this,z,c) { - std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T)); - std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); - std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T)); - } else cimg_forZC(*this,z,c) { - std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T)); - std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T)); - std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T)); - } - } break; - case 1 : // Neumann - if (delta_y<0) { - const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y; - if (!ndelta_y) return *this; - cimg_forZC(*this,z,c) { - std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); - T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c); - for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; } - } - } else { - const int ndelta_y = (delta_y>=height())?height() - 1:delta_y; - if (!ndelta_y) return *this; - cimg_forZC(*this,z,c) { - std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T)); - T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c); - for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; } - } - } - break; - default : // Dirichlet - if (delta_y<=-height() || delta_y>=height()) return fill((T)0); - if (delta_y<0) cimg_forZC(*this,z,c) { - std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T)); - std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T)); - } else cimg_forZC(*this,z,c) { - std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T)); - std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T)); - } - } - - if (delta_z) // Shift along Z-axis - switch (boundary_conditions) { - case 2 : { // Periodic - const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth()); - if (!ndelta_z) return *this; - CImg<T> buf(width(),height(),cimg::abs(ndelta_z)); - if (ndelta_z>0) cimg_forC(*this,c) { - std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T)); - std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); - std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T)); - } else cimg_forC(*this,c) { - std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T)); - std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T)); - std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T)); - } - } break; - case 1 : // Neumann - if (delta_z<0) { - const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z; - if (!ndelta_z) return *this; - cimg_forC(*this,c) { - std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); - T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c); - for (int l = 0; l<ndelta_z - 1; ++l) { - std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height; - } - } - } else { - const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z; - if (!ndelta_z) return *this; - cimg_forC(*this,c) { - std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); - T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c); - for (int l = 0; l<ndelta_z - 1; ++l) { - std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height; - } - } - } - break; - default : // Dirichlet - if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0); - if (delta_z<0) cimg_forC(*this,c) { - std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T)); - std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T)); - } else cimg_forC(*this,c) { - std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T)); - std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T)); - } - } - - if (delta_c) // Shift along C-axis - switch (boundary_conditions) { - case 2 : { // Periodic - const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum()); - if (!ndelta_c) return *this; - CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c)); - if (ndelta_c>0) { - std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T)); - std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); - std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T)); - } else { - std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T)); - std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T)); - std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T)); - } - } break; - case 1 : // Neumann - if (delta_c<0) { - const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c; - if (!ndelta_c) return *this; - std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); - T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1); - for (int l = 0; l<ndelta_c - 1; ++l) { - std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth; - } - } else { - const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c; - if (!ndelta_c) return *this; - std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); - T *ptrd = data(0,0,0,1); - for (int l = 0; l<ndelta_c - 1; ++l) { - std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth; - } - } - break; - default : // Dirichlet - if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0); - if (delta_c<0) { - std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T)); - std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T)); - } else { - std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T)); - std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T)); - } - } - return *this; - } - - //! Shift image content \newinstance. - CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, - const unsigned int boundary_conditions=0) const { - return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions); - } - - //! Permute axes order. - /** - \param order Axes permutations, as a C-string of 4 characters. - This function permutes image content regarding the specified axes permutation. - **/ - CImg<T>& permute_axes(const char *const order) { - return get_permute_axes(order).move_to(*this); - } - - //! Permute axes order \newinstance. - CImg<T> get_permute_axes(const char *const order) const { - const T foo = (T)0; - return _permute_axes(order,foo); - } - - template<typename t> - CImg<t> _permute_axes(const char *const order, const t&) const { - if (is_empty() || !order) return CImg<t>(*this,false); - CImg<t> res; - const T* ptrs = _data; - unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; - for (unsigned int l = 0; order[l]; ++l) { - int c = cimg::lowercase(order[l]); - if (c!='x' && c!='y' && c!='z' && c!='c') { *s_code = 4; break; } - else { ++n_code[c%=4]; s_code[l] = c; } - } - if (*order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { - const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); - ulongT wh, whd; - switch (code) { - case 0x0123 : // xyzc - return +*this; - case 0x0132 : // xycz - res.assign(_width,_height,_spectrum,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); - break; - case 0x0213 : // xzyc - res.assign(_width,_depth,_height,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); - break; - case 0x0231 : // xzcy - res.assign(_width,_depth,_spectrum,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); - break; - case 0x0312 : // xcyz - res.assign(_width,_spectrum,_height,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); - break; - case 0x0321 : // xczy - res.assign(_width,_spectrum,_depth,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); - break; - case 0x1023 : // yxzc - res.assign(_height,_width,_depth,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); - break; - case 0x1032 : // yxcz - res.assign(_height,_width,_spectrum,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); - break; - case 0x1203 : // yzxc - res.assign(_height,_depth,_width,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); - break; - case 0x1230 : // yzcx - res.assign(_height,_depth,_spectrum,_width); - switch (_width) { - case 1 : { - t *ptr_r = res.data(0,0,0,0); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)*(ptrs++); - } - } break; - case 2 : { - t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)ptrs[0]; - *(ptr_g++) = (t)ptrs[1]; - ptrs+=2; - } - } break; - case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB - t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)ptrs[0]; - *(ptr_g++) = (t)ptrs[1]; - *(ptr_b++) = (t)ptrs[2]; - ptrs+=3; - } - } break; - case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA - t - *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), - *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)ptrs[0]; - *(ptr_g++) = (t)ptrs[1]; - *(ptr_b++) = (t)ptrs[2]; - *(ptr_a++) = (t)ptrs[3]; - ptrs+=4; - } - } break; - default : { - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++); - return res; - } - } - break; - case 0x1302 : // ycxz - res.assign(_height,_spectrum,_width,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); - break; - case 0x1320 : // yczx - res.assign(_height,_spectrum,_depth,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); - break; - case 0x2013 : // zxyc - res.assign(_depth,_width,_height,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); - break; - case 0x2031 : // zxcy - res.assign(_depth,_width,_spectrum,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); - break; - case 0x2103 : // zyxc - res.assign(_depth,_height,_width,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); - break; - case 0x2130 : // zycx - res.assign(_depth,_height,_spectrum,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); - break; - case 0x2301 : // zcxy - res.assign(_depth,_spectrum,_width,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); - break; - case 0x2310 : // zcyx - res.assign(_depth,_spectrum,_height,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); - break; - case 0x3012 : // cxyz - res.assign(_spectrum,_width,_height,_depth); - switch (_spectrum) { - case 1 : { - const T *ptr_r = data(0,0,0,0); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++); - } break; - case 2 : { - const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { - ptrd[0] = (t)*(ptr_r++); - ptrd[1] = (t)*(ptr_g++); - ptrd+=2; - } - } break; - case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB - const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { - ptrd[0] = (t)*(ptr_r++); - ptrd[1] = (t)*(ptr_g++); - ptrd[2] = (t)*(ptr_b++); - ptrd+=3; - } - } break; - case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA - const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { - ptrd[0] = (t)*(ptr_r++); - ptrd[1] = (t)*(ptr_g++); - ptrd[2] = (t)*(ptr_b++); - ptrd[3] = (t)*(ptr_a++); - ptrd+=4; - } - } break; - default : { - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); - } - } - break; - case 0x3021 : // cxzy - res.assign(_spectrum,_width,_depth,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); - break; - case 0x3102 : // cyxz - res.assign(_spectrum,_height,_width,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); - break; - case 0x3120 : // cyzx - res.assign(_spectrum,_height,_depth,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); - break; - case 0x3201 : // czxy - res.assign(_spectrum,_depth,_width,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); - break; - case 0x3210 : // czyx - res.assign(_spectrum,_depth,_height,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); - break; - } - } - if (!res) - throw CImgArgumentException(_cimg_instance - "permute_axes(): Invalid specified permutation '%s'.", - cimg_instance, - order); - return res; - } - - //! Unroll pixel values along specified axis. - /** - \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c'). - **/ - CImg<T>& unroll(const char axis) { - const unsigned int siz = (unsigned int)size(); - if (siz) switch (cimg::lowercase(axis)) { - case 'x' : _width = siz; _height = _depth = _spectrum = 1; break; - case 'y' : _height = siz; _width = _depth = _spectrum = 1; break; - case 'z' : _depth = siz; _width = _height = _spectrum = 1; break; - default : _spectrum = siz; _width = _height = _depth = 1; - } - return *this; - } - - //! Unroll pixel values along specified axis \newinstance. - CImg<T> get_unroll(const char axis) const { - return (+*this).unroll(axis); - } - - //! Rotate image with arbitrary angle. - /** - \param angle Rotation angle, in degrees. - \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>. - \param boundary_conditions Boundary conditions. - Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>. - \note The size of the image is modified. - **/ - CImg<T>& rotate(const float angle, const unsigned int interpolation=1, - const unsigned int boundary_conditions=0) { - const float nangle = cimg::mod(angle,360.f); - if (nangle==0.f) return *this; - return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this); - } - - //! Rotate image with arbitrary angle \newinstance. - CImg<T> get_rotate(const float angle, const unsigned int interpolation=1, - const unsigned int boundary_conditions=0) const { - if (is_empty()) return *this; - CImg<T> res; - const float nangle = cimg::mod(angle,360.f); - if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles - const int wm1 = width() - 1, hm1 = height() - 1; - const int iangle = (int)nangle/90; - switch (iangle) { - case 1 : { // 90 deg - res.assign(_height,_width,_depth,_spectrum); - T *ptrd = res._data; - cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c); - } break; - case 2 : { // 180 deg - res.assign(_width,_height,_depth,_spectrum); - T *ptrd = res._data; - cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c); - } break; - case 3 : { // 270 deg - res.assign(_height,_width,_depth,_spectrum); - T *ptrd = res._data; - cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c); - } break; - default : // 0 deg - return *this; - } - } else { // Generic angle - const float - rad = (float)(nangle*cimg::PI/180.), - ca = (float)std::cos(rad), sa = (float)std::sin(rad), - ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa), - vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca), - w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1); - res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum); - const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1); - _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2); - } - return res; - } - - //! Rotate image with arbitrary angle, around a center point. - /** - \param angle Rotation angle, in degrees. - \param cx X-coordinate of the rotation center. - \param cy Y-coordinate of the rotation center. - \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>. - \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>. - **/ - CImg<T>& rotate(const float angle, const float cx, const float cy, - const unsigned int interpolation, const unsigned int boundary_conditions=0) { - return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this); - } - - //! Rotate image with arbitrary angle, around a center point \newinstance. - CImg<T> get_rotate(const float angle, const float cx, const float cy, - const unsigned int interpolation, const unsigned int boundary_conditions=0) const { - if (is_empty()) return *this; - CImg<T> res(_width,_height,_depth,_spectrum); - _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy); - return res; - } - - // [internal] Perform 2D rotation with arbitrary angle. - void _rotate(CImg<T>& res, const float angle, - const unsigned int interpolation, const unsigned int boundary_conditions, - const float w2, const float h2, - const float rw2, const float rh2) const { - const float - rad = (float)(angle*cimg::PI/180.), - ca = (float)std::cos(rad), sa = (float)std::sin(rad); - - switch (boundary_conditions) { - case 3 : { // Mirror - - switch (interpolation) { - case 2 : { // Cubic interpolation - const float ww = 2.f*width(), hh = 2.f*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2, - mx = cimg::mod(w2 + xc*ca + yc*sa,ww), - my = cimg::mod(h2 - xc*sa + yc*ca,hh); - res(x,y,z,c) = _cubic_cut_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c); - } - } break; - case 1 : { // Linear interpolation - const float ww = 2.f*width(), hh = 2.f*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2, - mx = cimg::mod(w2 + xc*ca + yc*sa,ww), - my = cimg::mod(h2 - xc*sa + yc*ca,hh); - res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c); - } - } break; - default : { // Nearest-neighbor interpolation - const int ww = 2*width(), hh = 2*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2, - mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww), - my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh); - res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c); - } - } - } - } break; - - case 2 : // Periodic - switch (interpolation) { - case 2 : { // Cubic interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = _cubic_cut_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()), - cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c); - } - } break; - case 1 : { // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()), - cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c); - } - } break; - default : { // Nearest-neighbor interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()), - cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c); - } - } - } break; - - case 1 : // Neumann - switch (interpolation) { - case 2 : { // Cubic interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = _cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c); - } - } break; - case 1 : { // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c); - } - } break; - default : { // Nearest-neighbor interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa), - (int)cimg::round(h2 - xc*sa + yc*ca),z,c); - } - } - } break; - - default : // Dirichlet - switch (interpolation) { - case 2 : { // Cubic interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0); - } - } break; - case 1 : { // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0); - } - } break; - default : { // Nearest-neighbor interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZC(res,x,y,z,c) { - const float xc = x - rw2, yc = y - rh2; - res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa), - (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0); - } - } - } - } - } - - //! Rotate volumetric image with arbitrary angle and axis. - /** - \param u X-coordinate of the 3D rotation axis. - \param v Y-coordinate of the 3D rotation axis. - \param w Z-coordinate of the 3D rotation axis. - \param angle Rotation angle, in degrees. - \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>. - \param boundary_conditions Boundary conditions. - Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>. - \note Most of the time, size of the image is modified. - **/ - CImg<T> rotate(const float u, const float v, const float w, const float angle, - const unsigned int interpolation, const unsigned int boundary_conditions) { - const float nangle = cimg::mod(angle,360.f); - if (nangle==0.f) return *this; - return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this); - } - - //! Rotate volumetric image with arbitrary angle and axis \newinstance. - CImg<T> get_rotate(const float u, const float v, const float w, const float angle, - const unsigned int interpolation, const unsigned int boundary_conditions) const { - if (is_empty()) return *this; - CImg<T> res; - const float - w1 = _width - 1, h1 = _height - 1, d1 = _depth -1, - w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1; - CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle); - const CImg<Tfloat> - X = R*CImg<Tfloat>(8,3,1,1, - 0.f,w1,w1,0.f,0.f,w1,w1,0.f, - 0.f,0.f,h1,h1,0.f,0.f,h1,h1, - 0.f,0.f,0.f,0.f,d1,d1,d1,d1); - float - xm, xM = X.get_shared_row(0).max_min(xm), - ym, yM = X.get_shared_row(1).max_min(ym), - zm, zM = X.get_shared_row(2).max_min(zm); - const int - dx = (int)cimg::round(xM - xm), - dy = (int)cimg::round(yM - ym), - dz = (int)cimg::round(zM - zm); - R.transpose(); - res.assign(1 + dx,1 + dy,1 + dz,_spectrum); - const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz; - _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2); - return res; - } - - //! Rotate volumetric image with arbitrary angle and axis, around a center point. - /** - \param u X-coordinate of the 3D rotation axis. - \param v Y-coordinate of the 3D rotation axis. - \param w Z-coordinate of the 3D rotation axis. - \param angle Rotation angle, in degrees. - \param cx X-coordinate of the rotation center. - \param cy Y-coordinate of the rotation center. - \param cz Z-coordinate of the rotation center. - \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>. - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic }</tt>. - \note Most of the time, size of the image is modified. - **/ - CImg<T> rotate(const float u, const float v, const float w, const float angle, - const float cx, const float cy, const float cz, - const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { - const float nangle = cimg::mod(angle,360.f); - if (nangle==0.f) return *this; - return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this); - } - - //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance. - CImg<T> get_rotate(const float u, const float v, const float w, const float angle, - const float cx, const float cy, const float cz, - const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { - if (is_empty()) return *this; - CImg<T> res(_width,_height,_depth,_spectrum); - CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle); - _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz); - return res; - } - - // [internal] Perform 3D rotation with arbitrary axis and angle. - void _rotate(CImg<T>& res, const CImg<Tfloat>& R, - const unsigned int interpolation, const unsigned int boundary_conditions, - const float w2, const float h2, const float d2, - const float rw2, const float rh2, const float rd2) const { - switch (boundary_conditions) { - case 3 : // Mirror - switch (interpolation) { - case 2 : { // Cubic interpolation - const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth(); - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww), - Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh), - Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd); - cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X<width()?X:ww - X - 1, - Y<height()?Y:hh - Y - 1, - Z<depth()?Z:dd - Z - z,c); - } - } break; - case 1 : { // Linear interpolation - const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth(); - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww), - Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh), - Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd); - cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1, - Y<height()?Y:hh - Y - 1, - Z<depth()?Z:dd - Z - 1,c); - } - } break; - default : { // Nearest-neighbor interpolation - const int ww = 2*width(), hh = 2*height(), dd = 2*depth(); - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float xc = x - rw2, yc = y - rh2, zc = z - rd2; - const int - X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww), - Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh), - Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd); - cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1, - Y<height()?Y:hh - Y - 1, - Z<depth()?Z:dd - Z - 1,c); - } - } - } break; - - case 2 : // Periodic - switch (interpolation) { - case 2 : { // Cubic interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()), - Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()), - Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth()); - cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c); - } - } break; - case 1 : { // Linear interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()), - Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()), - Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth()); - cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X,Y,Z,c); - } - } break; - default : { // Nearest-neighbor interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float xc = x - rw2, yc = y - rh2, zc = z - rd2; - const int - X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()), - Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()), - Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth()); - cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c); - } - } - } break; - - case 1 : // Neumann - switch (interpolation) { - case 2 : { // Cubic interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc, - Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc, - Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc; - cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c); - } - } break; - case 1 : { // Linear interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc, - Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc, - Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc; - cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c); - } - } break; - default : { // Nearest-neighbor interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float xc = x - rw2, yc = y - rh2, zc = z - rd2; - const int - X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc), - Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc), - Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc); - cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c); - } - } - } break; - - default : // Dirichlet - switch (interpolation) { - case 2 : { // Cubic interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc, - Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc, - Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc; - cimg_forC(res,c) res(x,y,z,c) = cubic_cut_atXYZ(X,Y,Z,c,(T)0); - } - } break; - case 1 : { // Linear interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float - xc = x - rw2, yc = y - rh2, zc = z - rd2, - X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc, - Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc, - Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc; - cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0); - } - } break; - default : { // Nearest-neighbor interpolation - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(res.size(),2048)) - cimg_forXYZ(res,x,y,z) { - const float xc = x - rw2, yc = y - rh2, zc = z - rd2; - const int - X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc), - Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc), - Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc); - cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0); - } - } - } break; - } - } - - //! Warp image content by a warping field. - /** - \param warp Warping field. - \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative } - \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>. - \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>. - **/ - template<typename t> - CImg<T>& warp(const CImg<t>& warp, const unsigned int mode=0, - const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { - return get_warp(warp,mode,interpolation,boundary_conditions).move_to(*this); - } - - //! Warp image content by a warping field \newinstance - template<typename t> - CImg<T> get_warp(const CImg<t>& warp, const unsigned int mode=0, - const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { - if (is_empty() || !warp) return *this; - if (mode && !is_sameXYZ(warp)) - throw CImgArgumentException(_cimg_instance - "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) " - "have different XYZ dimensions.", - cimg_instance, - warp._width,warp._height,warp._depth,warp._spectrum,warp._data); - - CImg<T> res(warp._width,warp._height,warp._depth,_spectrum); - - if (warp._spectrum==1) { // 1D warping - if (mode>=3) { // Forward-relative warp - res.fill((T)0); - if (interpolation>=1) // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c); - } - else // Nearest-neighbor interpolation - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = x + (int)cimg::round(*(ptrs0++)); - if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++); - } - } - } else if (mode==2) { // Forward-absolute warp - res.fill((T)0); - if (interpolation>=1) // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c); - } - else // Nearest-neighbor interpolation - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = (int)cimg::round(*(ptrs0++)); - if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++); - } - } - } else if (mode==1) { // Backward-relative warp - if (interpolation==2) // Cubic interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float mx = cimg::mod(x - (float)*(ptrs0++),w2); - *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,y,z,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(x - (float)*(ptrs0++),y,z,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = cubic_cut_atX(x - (float)*(ptrs0++),y,z,c,(T)0); - } - } - else if (interpolation==1) // Linear interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float mx = cimg::mod(x - (float)*(ptrs0++),w2); - *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0); - } - } - else // Nearest-neighbor interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const int w2 = 2*width(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2); - *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c); - } - } - } break; - case 2 : // Periodic - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),y,z,c); - } - break; - case 1 : // Neumann - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c); - } - break; - default : // Dirichlet - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,(T)0); - } - } - } - else { // Backward-absolute warp - if (interpolation==2) // Cubic interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float mx = cimg::mod((float)*(ptrs0++),w2); - *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,0,0,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX((float)*(ptrs0++),0,0,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = cubic_cut_atX((float)*(ptrs0++),0,0,c,(T)0); - } - } - else if (interpolation==1) // Linear interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float mx = cimg::mod((float)*(ptrs0++),w2); - *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0); - } - } - else // Nearest-neighbor interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const int w2 = 2*width(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2); - *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c); - } - } - } break; - case 2 : // Periodic - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),0,0,c); - } - break; - case 1 : // Neumann - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atX((int)*(ptrs0++),0,0,c); - } - break; - default : // Dirichlet - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atX((int)*(ptrs0++),0,0,c,(T)0); - } - } - } - - } else if (warp._spectrum==2) { // 2D warping - if (mode>=3) { // Forward-relative warp - res.fill((T)0); - if (interpolation>=1) // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c); - } - else // Nearest-neighbor interpolation - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++)); - if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++); - } - } - } else if (mode==2) { // Forward-absolute warp - res.fill((T)0); - if (interpolation>=1) // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c); - } - else // Nearest-neighbor interpolation - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++)); - if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++); - } - } - } else if (mode==1) { // Backward-relative warp - if (interpolation==2) // Cubic interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod(x - (float)*(ptrs0++),w2), - my = cimg::mod(y - (float)*(ptrs1++),h2); - *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width), - cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0); - } - } - else if (interpolation==1) // Linear interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod(x - (float)*(ptrs0++),w2), - my = cimg::mod(y - (float)*(ptrs1++),h2); - *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width), - cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0); - } - } - else // Nearest-neighbor interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const int w2 = 2*width(), h2 = 2*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const int - mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2), - my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2); - *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c); - } - } - } break; - case 2 : // Periodic - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width), - cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height),z,c); - } - break; - case 1 : // Neumann - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c); - } - break; - default : // Dirichlet - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,(T)0); - } - } - } else { // Backward-absolute warp - if (interpolation==2) // Cubic interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod((float)*(ptrs0++),w2), - my = cimg::mod((float)*(ptrs1++),h2); - *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod((float)*(ptrs0++),(float)_width), - cimg::mod((float)*(ptrs1++),(float)_height),0,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0); - } - } - else if (interpolation==1) // Linear interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod((float)*(ptrs0++),w2), - my = cimg::mod((float)*(ptrs1++),h2); - *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width), - cimg::mod((float)*(ptrs1++),(float)_height),0,c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0); - } - } - else // Nearest-neighbor interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const int w2 = 2*width(), h2 = 2*height(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const int - mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2), - my = cimg::mod((int)cimg::round(*(ptrs1++)),h2); - *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c); - } - } - } break; - case 2 : // Periodic - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width), - cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height),0,c); - } - break; - case 1 : // Neumann - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c); - } - break; - default : // Dirichlet - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,(T)0); - } - } - } - - } else { // 3D warping - if (mode>=3) { // Forward-relative warp - res.fill((T)0); - if (interpolation>=1) // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++), - z + (float)*(ptrs2++),c); - } - else // Nearest-neighbor interpolation - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int - X = x + (int)cimg::round(*(ptrs0++)), - Y = y + (int)cimg::round(*(ptrs1++)), - Z = z + (int)cimg::round(*(ptrs2++)); - if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++); - } - } - } else if (mode==2) { // Forward-absolute warp - res.fill((T)0); - if (interpolation>=1) // Linear interpolation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); - } - else // Nearest-neighbor interpolation - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - const T *ptrs = data(0,y,z,c); - cimg_forX(res,x) { - const int - X = (int)cimg::round(*(ptrs0++)), - Y = (int)cimg::round(*(ptrs1++)), - Z = (int)cimg::round(*(ptrs2++)); - if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++); - } - } - } else if (mode==1) { // Backward-relative warp - if (interpolation==2) // Cubic interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod(x - (float)*(ptrs0++),w2), - my = cimg::mod(y - (float)*(ptrs1++),h2), - mz = cimg::mod(z - (float)*(ptrs2++),d2); - *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width), - cimg::mod(y - (float)*(ptrs1++),(float)_height), - cimg::mod(z - (float)*(ptrs2++),(float)_depth),c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(ptrd++) = _cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(ptrd++) = cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0); - } - } - else if (interpolation==1) // Linear interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod(x - (float)*(ptrs0++),w2), - my = cimg::mod(y - (float)*(ptrs1++),h2), - mz = cimg::mod(z - (float)*(ptrs2++),d2); - *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width), - cimg::mod(y - (float)*(ptrs1++),(float)_height), - cimg::mod(z - (float)*(ptrs2++),(float)_depth),c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) - *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0); - } - } - else // Nearest neighbor interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const int - mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2), - my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2), - mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2); - *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1,c); - } - } - } break; - case 2 : // Periodic - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width), - cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height), - cimg::mod(z - (int)cimg::round(*(ptrs2++)),(int)_depth),c); - } - break; - case 1 : // Neumann - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c); - } - break; - default : // Dirichlet - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,(T)0); - } - } - } else { // Backward-absolute warp - if (interpolation==2) // Cubic interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod((float)*(ptrs0++),w2), - my = cimg::mod((float)*(ptrs1++),h2), - mz = cimg::mod((float)*(ptrs2++),d2); - *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1,c); - } - } - } break; - case 2 : // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width), - cimg::mod((float)*(ptrs1++),(float)_height), - cimg::mod((float)*(ptrs2++),(float)_depth),c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++), - c,(T)0); - } - } - else if (interpolation==1) // Linear interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const float - mx = cimg::mod((float)*(ptrs0++),w2), - my = cimg::mod((float)*(ptrs1++),h2), - mz = cimg::mod((float)*(ptrs2++),d2); - *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1,c); - } - } - } break; - case 2 :// Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width), - cimg::mod((float)*(ptrs1++),(float)_height), - cimg::mod((float)*(ptrs2++),(float)_depth),c); - } - break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); - } - break; - default : // Dirichlet - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),1048576)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++), - c,(T)0); - } - } - else // Nearest-neighbor interpolation - switch (boundary_conditions) { - case 3 : { // Mirror - const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(res.size(),4096)) - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) { - const int - mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2), - my = cimg::mod((int)cimg::round(*(ptrs1++)),h2), - mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2); - *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1,c); - } - } - } break; - case 2 : // Periodic - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width), - cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height), - cimg::mod((int)cimg::round(*(ptrs2++)),(int)_depth),c); - } - break; - case 1 : // Neumann - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c); - } - break; - default : // Dirichlet - cimg_forYZC(res,y,z,c) { - const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2); - T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,(T)0); - } - } - } - } - return res; - } - - //! Generate a 2D representation of a 3D image, with XY,XZ and YZ views. - /** - \param x0 X-coordinate of the projection point. - \param y0 Y-coordinate of the projection point. - \param z0 Z-coordinate of the projection point. - **/ - CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const { - if (is_empty() || _depth<2) return +*this; - const unsigned int - _x0 = (x0>=_width)?_width - 1:x0, - _y0 = (y0>=_height)?_height - 1:y0, - _z0 = (z0>=_depth)?_depth - 1:z0; - const CImg<T> - img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1), - img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc"). - resize(_depth,_height,1,-100,-1), - img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1); - return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())). - draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy). - draw_image(0,img_xy._height,img_xz); - } - - //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace. - CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) { - if (_depth<2) return *this; - return get_projections2d(x0,y0,z0).move_to(*this); - } - - //! Crop image region. - /** - \param x0 = X-coordinate of the upper-left crop rectangle corner. - \param y0 = Y-coordinate of the upper-left crop rectangle corner. - \param z0 = Z-coordinate of the upper-left crop rectangle corner. - \param c0 = C-coordinate of the upper-left crop rectangle corner. - \param x1 = X-coordinate of the lower-right crop rectangle corner. - \param y1 = Y-coordinate of the lower-right crop rectangle corner. - \param z1 = Z-coordinate of the lower-right crop rectangle corner. - \param c1 = C-coordinate of the lower-right crop rectangle corner. - \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. - **/ - CImg<T>& crop(const int x0, const int y0, const int z0, const int c0, - const int x1, const int y1, const int z1, const int c1, - const unsigned int boundary_conditions=0) { - return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this); - } - - //! Crop image region \newinstance. - CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0, - const int x1, const int y1, const int z1, const int c1, - const unsigned int boundary_conditions=0) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "crop(): Empty instance.", - cimg_instance); - const int - nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0, - ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0, - nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0, - nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0; - CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0); - if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) - switch (boundary_conditions) { - case 3 : { // Mirror - const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && - _height*_depth*_spectrum>=4)) - cimg_forXYZC(res,x,y,z,c) { - const int - mx = cimg::mod(nx0 + x,w2), - my = cimg::mod(ny0 + y,h2), - mz = cimg::mod(nz0 + z,d2), - mc = cimg::mod(nc0 + c,s2); - res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1, - my<height()?my:h2 - my - 1, - mz<depth()?mz:d2 - mz - 1, - mc<spectrum()?mc:s2 - mc - 1); - } - } break; - case 2 : { // Periodic - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && - _height*_depth*_spectrum>=4)) - cimg_forXYZC(res,x,y,z,c) { - res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()), - cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum())); - } - } break; - case 1 : // Neumann - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && - _height*_depth*_spectrum>=4)) - cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c); - break; - default : // Dirichlet - res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this); - } - else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this); - return res; - } - - //! Crop image region \overloading. - CImg<T>& crop(const int x0, const int y0, const int z0, - const int x1, const int y1, const int z1, - const unsigned int boundary_conditions=0) { - return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); - } - - //! Crop image region \newinstance. - CImg<T> get_crop(const int x0, const int y0, const int z0, - const int x1, const int y1, const int z1, - const unsigned int boundary_conditions=0) const { - return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); - } - - //! Crop image region \overloading. - CImg<T>& crop(const int x0, const int y0, - const int x1, const int y1, - const unsigned int boundary_conditions=0) { - return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); - } - - //! Crop image region \newinstance. - CImg<T> get_crop(const int x0, const int y0, - const int x1, const int y1, - const unsigned int boundary_conditions=0) const { - return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); - } - - //! Crop image region \overloading. - CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) { - return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); - } - - //! Crop image region \newinstance. - CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const { - return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); - } - - //! Autocrop image region, regarding the specified background value. - CImg<T>& autocrop(const T& value, const char *const axes="czyx") { - if (is_empty()) return *this; - for (const char *s = axes; *s; ++s) { - const char axis = cimg::lowercase(*s); - const CImg<intT> coords = _autocrop(value,axis); - if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels - else switch (axis) { - case 'x' : { - const int x0 = coords[0], x1 = coords[1]; - if (x0>=0 && x1>=0) crop(x0,x1); - } break; - case 'y' : { - const int y0 = coords[0], y1 = coords[1]; - if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1); - } break; - case 'z' : { - const int z0 = coords[0], z1 = coords[1]; - if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1); - } break; - default : { - const int c0 = coords[0], c1 = coords[1]; - if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1); - } - } - } - return *this; - } - - //! Autocrop image region, regarding the specified background value \newinstance. - CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const { - return (+*this).autocrop(value,axes); - } - - //! Autocrop image region, regarding the specified background color. - /** - \param color Color used for the crop. If \c 0, color is guessed. - \param axes Axes used for the crop. - **/ - CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") { - if (is_empty()) return *this; - if (!color) { // Guess color - const CImg<T> col1 = get_vector_at(0,0,0); - const unsigned int w = _width, h = _height, d = _depth, s = _spectrum; - autocrop(col1,axes); - if (_width==w && _height==h && _depth==d && _spectrum==s) { - const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1); - autocrop(col2,axes); - } - return *this; - } - for (const char *s = axes; *s; ++s) { - const char axis = cimg::lowercase(*s); - switch (axis) { - case 'x' : { - int x0 = width(), x1 = -1; - cimg_forC(*this,c) { - const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x'); - const int nx0 = coords[0], nx1 = coords[1]; - if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); } - } - if (x0==width() && x1==-1) return assign(); else crop(x0,x1); - } break; - case 'y' : { - int y0 = height(), y1 = -1; - cimg_forC(*this,c) { - const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y'); - const int ny0 = coords[0], ny1 = coords[1]; - if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); } - } - if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1); - } break; - default : { - int z0 = depth(), z1 = -1; - cimg_forC(*this,c) { - const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z'); - const int nz0 = coords[0], nz1 = coords[1]; - if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); } - } - if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1); - } - } - } - return *this; - } - - //! Autocrop image region, regarding the specified background color \newinstance. - CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const { - return (+*this).autocrop(color,axes); - } - - //! Autocrop image region, regarding the specified background color \overloading. - template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") { - return get_autocrop(color,axes).move_to(*this); - } - - //! Autocrop image region, regarding the specified background color \newinstance. - template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const { - return get_autocrop(color._data,axes); - } - - CImg<intT> _autocrop(const T& value, const char axis) const { - CImg<intT> res; - switch (cimg::lowercase(axis)) { - case 'x' : { - int x0 = -1, x1 = -1; - cimg_forX(*this,x) cimg_forYZC(*this,y,z,c) - if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); } - if (x0>=0) { - for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c) - if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); } - } - res = CImg<intT>::vector(x0,x1); - } break; - case 'y' : { - int y0 = -1, y1 = -1; - cimg_forY(*this,y) cimg_forXZC(*this,x,z,c) - if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); } - if (y0>=0) { - for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c) - if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); } - } - res = CImg<intT>::vector(y0,y1); - } break; - case 'z' : { - int z0 = -1, z1 = -1; - cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c) - if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); } - if (z0>=0) { - for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c) - if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); } - } - res = CImg<intT>::vector(z0,z1); - } break; - default : { - int c0 = -1, c1 = -1; - cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z) - if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); } - if (c0>=0) { - for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z) - if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; } - } - res = CImg<intT>::vector(c0,c1); - } - } - return res; - } - - //! Return specified image column. - /** - \param x0 Image column. - **/ - CImg<T> get_column(const int x0) const { - return get_columns(x0,x0); - } - - //! Return specified image column \inplace. - CImg<T>& column(const int x0) { - return columns(x0,x0); - } - - //! Return specified range of image columns. - /** - \param x0 Starting image column. - \param x1 Ending image column. - **/ - CImg<T>& columns(const int x0, const int x1) { - return get_columns(x0,x1).move_to(*this); - } - - //! Return specified range of image columns \inplace. - CImg<T> get_columns(const int x0, const int x1) const { - return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1); - } - - //! Return specified image row. - CImg<T> get_row(const int y0) const { - return get_rows(y0,y0); - } - - //! Return specified image row \inplace. - /** - \param y0 Image row. - **/ - CImg<T>& row(const int y0) { - return rows(y0,y0); - } - - //! Return specified range of image rows. - /** - \param y0 Starting image row. - \param y1 Ending image row. - **/ - CImg<T> get_rows(const int y0, const int y1) const { - return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1); - } - - //! Return specified range of image rows \inplace. - CImg<T>& rows(const int y0, const int y1) { - return get_rows(y0,y1).move_to(*this); - } - - //! Return specified image slice. - /** - \param z0 Image slice. - **/ - CImg<T> get_slice(const int z0) const { - return get_slices(z0,z0); - } - - //! Return specified image slice \inplace. - CImg<T>& slice(const int z0) { - return slices(z0,z0); - } - - //! Return specified range of image slices. - /** - \param z0 Starting image slice. - \param z1 Ending image slice. - **/ - CImg<T> get_slices(const int z0, const int z1) const { - return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1); - } - - //! Return specified range of image slices \inplace. - CImg<T>& slices(const int z0, const int z1) { - return get_slices(z0,z1).move_to(*this); - } - - //! Return specified image channel. - /** - \param c0 Image channel. - **/ - CImg<T> get_channel(const int c0) const { - return get_channels(c0,c0); - } - - //! Return specified image channel \inplace. - CImg<T>& channel(const int c0) { - return channels(c0,c0); - } - - //! Return specified range of image channels. - /** - \param c0 Starting image channel. - \param c1 Ending image channel. - **/ - CImg<T> get_channels(const int c0, const int c1) const { - return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1); - } - - //! Return specified range of image channels \inplace. - CImg<T>& channels(const int c0, const int c1) { - return get_channels(c0,c1).move_to(*this); - } - - //! Return stream line of a 2D or 3D vector field. - CImg<floatT> get_streamline(const float x, const float y, const float z, - const float L=256, const float dl=0.1f, - const unsigned int interpolation_type=2, const bool is_backward_tracking=false, - const bool is_oriented_only=false) const { - if (_spectrum!=2 && _spectrum!=3) - throw CImgInstanceException(_cimg_instance - "streamline(): Instance is not a 2D or 3D vector field.", - cimg_instance); - if (_spectrum==2) { - if (is_oriented_only) { - typename CImg<T>::_functor4d_streamline2d_oriented func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, - 0,0,0,_width - 1.f,_height - 1.f,0.f); - } else { - typename CImg<T>::_functor4d_streamline2d_directed func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, - 0,0,0,_width - 1.f,_height - 1.f,0.f); - } - } - if (is_oriented_only) { - typename CImg<T>::_functor4d_streamline3d_oriented func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, - 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); - } - typename CImg<T>::_functor4d_streamline3d_directed func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, - 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); - } - - //! Return stream line of a 3D vector field. - /** - \param func Vector field function. - \param x X-coordinate of the starting point of the streamline. - \param y Y-coordinate of the starting point of the streamline. - \param z Z-coordinate of the starting point of the streamline. - \param L Streamline length. - \param dl Streamline length increment. - \param interpolation_type Type of interpolation. - Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>. - \param is_backward_tracking Tells if the streamline is estimated forward or backward. - \param is_oriented_only Tells if the direction of the vectors must be ignored. - \param x0 X-coordinate of the first bounding-box vertex. - \param y0 Y-coordinate of the first bounding-box vertex. - \param z0 Z-coordinate of the first bounding-box vertex. - \param x1 X-coordinate of the second bounding-box vertex. - \param y1 Y-coordinate of the second bounding-box vertex. - \param z1 Z-coordinate of the second bounding-box vertex. - **/ - template<typename tfunc> - static CImg<floatT> streamline(const tfunc& func, - const float x, const float y, const float z, - const float L=256, const float dl=0.1f, - const unsigned int interpolation_type=2, const bool is_backward_tracking=false, - const bool is_oriented_only=false, - const float x0=0, const float y0=0, const float z0=0, - const float x1=0, const float y1=0, const float z1=0) { - if (dl<=0) - throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g " - "(should be >0).", - pixel_type(), - dl); - - const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1); - if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>(); - const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1); - CImg<floatT> coordinates(size_L,3); - const float dl2 = dl/2; - float - *ptr_x = coordinates.data(0,0), - *ptr_y = coordinates.data(0,1), - *ptr_z = coordinates.data(0,2), - pu = (float)(dl*func(x,y,z,0)), - pv = (float)(dl*func(x,y,z,1)), - pw = (float)(dl*func(x,y,z,2)), - X = x, Y = y, Z = z; - - switch (interpolation_type) { - case 0 : { // Nearest integer interpolation - cimg_forX(coordinates,l) { - *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; - const int - xi = (int)(X>0?X + 0.5f:X - 0.5f), - yi = (int)(Y>0?Y + 0.5f:Y - 0.5f), - zi = (int)(Z>0?Z + 0.5f:Z - 0.5f); - float - u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)), - v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)), - w = (float)(dl*func((float)xi,(float)yi,(float)zi,2)); - if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } - if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } - if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break; - } - } break; - case 1 : { // First-order interpolation - cimg_forX(coordinates,l) { - *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; - float - u = (float)(dl*func(X,Y,Z,0)), - v = (float)(dl*func(X,Y,Z,1)), - w = (float)(dl*func(X,Y,Z,2)); - if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } - if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } - if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break; - } - } break; - case 2 : { // Second order interpolation - cimg_forX(coordinates,l) { - *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; - float - u0 = (float)(dl2*func(X,Y,Z,0)), - v0 = (float)(dl2*func(X,Y,Z,1)), - w0 = (float)(dl2*func(X,Y,Z,2)); - if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } - float - u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)), - v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)), - w = (float)(dl*func(X + u0,Y + v0,Z + w0,2)); - if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } - if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } - if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break; - } - } break; - default : { // Fourth order interpolation - cimg_forX(coordinates,x) { - *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; - float - u0 = (float)(dl2*func(X,Y,Z,0)), - v0 = (float)(dl2*func(X,Y,Z,1)), - w0 = (float)(dl2*func(X,Y,Z,2)); - if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } - float - u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)), - v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)), - w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2)); - if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; } - float - u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)), - v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)), - w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2)); - if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; } - float - u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)), - v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)), - w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2)); - if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; } - const float - u = (u0 + u3)/3 + (u1 + u2)/1.5f, - v = (v0 + v3)/3 + (v1 + v2)/1.5f, - w = (w0 + w3)/3 + (w1 + w2)/1.5f; - if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } - if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break; - } - } - } - if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0); - return coordinates; - } - - //! Return stream line of a 3D vector field \overloading. - static CImg<floatT> streamline(const char *const expression, - const float x, const float y, const float z, - const float L=256, const float dl=0.1f, - const unsigned int interpolation_type=2, const bool is_backward_tracking=true, - const bool is_oriented_only=false, - const float x0=0, const float y0=0, const float z0=0, - const float x1=0, const float y1=0, const float z1=0) { - _functor4d_streamline_expr func(expression); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1); - } - - struct _functor4d_streamline2d_directed { - const CImg<T>& ref; - _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {} - float operator()(const float x, const float y, const float z, const unsigned int c) const { - return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0; - } - }; - - struct _functor4d_streamline3d_directed { - const CImg<T>& ref; - _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {} - float operator()(const float x, const float y, const float z, const unsigned int c) const { - return (float)ref._linear_atXYZ(x,y,z,c); - } - }; - - struct _functor4d_streamline2d_oriented { - const CImg<T>& ref; - CImg<floatT> *pI; - _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); } - ~_functor4d_streamline2d_oriented() { delete pI; } - float operator()(const float x, const float y, const float z, const unsigned int c) const { -#define _cimg_vecalign2d(i,j) \ - if (I(i,j,0)*I(0,0,0) + I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); } - int - xi = (int)x - (x>=0?0:1), nxi = xi + 1, - yi = (int)y - (y>=0?0:1), nyi = yi + 1, - zi = (int)z; - const float - dx = x - xi, - dy = y - yi; - if (c==0) { - CImg<floatT>& I = *pI; - if (xi<0) xi = 0; - if (nxi<0) nxi = 0; - if (xi>=ref.width()) xi = ref.width() - 1; - if (nxi>=ref.width()) nxi = ref.width() - 1; - if (yi<0) yi = 0; - if (nyi<0) nyi = 0; - if (yi>=ref.height()) yi = ref.height() - 1; - if (nyi>=ref.height()) nyi = ref.height() - 1; - I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1); - I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1); - I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1); - I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1); - _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1); - } - return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0; - } - }; - - struct _functor4d_streamline3d_oriented { - const CImg<T>& ref; - CImg<floatT> *pI; - _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); } - ~_functor4d_streamline3d_oriented() { delete pI; } - float operator()(const float x, const float y, const float z, const unsigned int c) const { -#define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0) + I(i,j,k,1)*I(0,0,0,1) + I(i,j,k,2)*I(0,0,0,2)<0) { \ - I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); } - int - xi = (int)x - (x>=0?0:1), nxi = xi + 1, - yi = (int)y - (y>=0?0:1), nyi = yi + 1, - zi = (int)z - (z>=0?0:1), nzi = zi + 1; - const float - dx = x - xi, - dy = y - yi, - dz = z - zi; - if (c==0) { - CImg<floatT>& I = *pI; - if (xi<0) xi = 0; - if (nxi<0) nxi = 0; - if (xi>=ref.width()) xi = ref.width() - 1; - if (nxi>=ref.width()) nxi = ref.width() - 1; - if (yi<0) yi = 0; - if (nyi<0) nyi = 0; - if (yi>=ref.height()) yi = ref.height() - 1; - if (nyi>=ref.height()) nyi = ref.height() - 1; - if (zi<0) zi = 0; - if (nzi<0) nzi = 0; - if (zi>=ref.depth()) zi = ref.depth() - 1; - if (nzi>=ref.depth()) nzi = ref.depth() - 1; - I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); - I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0); - I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2); - I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); - I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0); - I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2); - I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1); - I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0); - I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2); - I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1); - I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0); - I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2); - _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0); - _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1); - } - return (float)pI->_linear_atXYZ(dx,dy,dz,c); - } - }; - - struct _functor4d_streamline_expr { - _cimg_math_parser *mp; - ~_functor4d_streamline_expr() { mp->end(); delete mp; } - _functor4d_streamline_expr(const char *const expr):mp(0) { - mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0); - } - float operator()(const float x, const float y, const float z, const unsigned int c) const { - return (float)(*mp)(x,y,z,c); - } - }; - - //! Return a shared-memory image referencing a range of pixels of the image instance. - /** - \param x0 X-coordinate of the starting pixel. - \param x1 X-coordinate of the ending pixel. - \param y0 Y-coordinate. - \param z0 Z-coordinate. - \param c0 C-coordinate. - **/ - CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1, - const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) { - const unsigned int - beg = (unsigned int)offset(x0,y0,z0,c0), - end = (unsigned int)offset(x1,y0,z0,c0); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", - cimg_instance, - x0,x1,y0,z0,c0); - - return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true); - } - - //! Return a shared-memory image referencing a range of pixels of the image instance \const. - const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1, - const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const { - const unsigned int - beg = (unsigned int)offset(x0,y0,z0,c0), - end = (unsigned int)offset(x1,y0,z0,c0); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", - cimg_instance, - x0,x1,y0,z0,c0); - - return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true); - } - - //! Return a shared-memory image referencing a range of rows of the image instance. - /** - \param y0 Y-coordinate of the starting row. - \param y1 Y-coordinate of the ending row. - \param z0 Z-coordinate. - \param c0 C-coordinate. - **/ - CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1, - const unsigned int z0=0, const unsigned int c0=0) { - const unsigned int - beg = (unsigned int)offset(0,y0,z0,c0), - end = (unsigned int)offset(0,y1,z0,c0); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_rows(): Invalid request of a shared-memory subset " - "(0->%u,%u->%u,%u,%u).", - cimg_instance, - _width - 1,y0,y1,z0,c0); - - return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true); - } - - //! Return a shared-memory image referencing a range of rows of the image instance \const. - const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1, - const unsigned int z0=0, const unsigned int c0=0) const { - const unsigned int - beg = (unsigned int)offset(0,y0,z0,c0), - end = (unsigned int)offset(0,y1,z0,c0); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_rows(): Invalid request of a shared-memory subset " - "(0->%u,%u->%u,%u,%u).", - cimg_instance, - _width - 1,y0,y1,z0,c0); - - return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true); - } - - //! Return a shared-memory image referencing one row of the image instance. - /** - \param y0 Y-coordinate. - \param z0 Z-coordinate. - \param c0 C-coordinate. - **/ - CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) { - return get_shared_rows(y0,y0,z0,c0); - } - - //! Return a shared-memory image referencing one row of the image instance \const. - const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const { - return get_shared_rows(y0,y0,z0,c0); - } - - //! Return a shared memory image referencing a range of slices of the image instance. - /** - \param z0 Z-coordinate of the starting slice. - \param z1 Z-coordinate of the ending slice. - \param c0 C-coordinate. - **/ - CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) { - const unsigned int - beg = (unsigned int)offset(0,0,z0,c0), - end = (unsigned int)offset(0,0,z1,c0); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_slices(): Invalid request of a shared-memory subset " - "(0->%u,0->%u,%u->%u,%u).", - cimg_instance, - _width - 1,_height - 1,z0,z1,c0); - - return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true); - } - - //! Return a shared memory image referencing a range of slices of the image instance \const. - const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const { - const unsigned int - beg = (unsigned int)offset(0,0,z0,c0), - end = (unsigned int)offset(0,0,z1,c0); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_slices(): Invalid request of a shared-memory subset " - "(0->%u,0->%u,%u->%u,%u).", - cimg_instance, - _width - 1,_height - 1,z0,z1,c0); - - return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true); - } - - //! Return a shared-memory image referencing one slice of the image instance. - /** - \param z0 Z-coordinate. - \param c0 C-coordinate. - **/ - CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) { - return get_shared_slices(z0,z0,c0); - } - - //! Return a shared-memory image referencing one slice of the image instance \const. - const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const { - return get_shared_slices(z0,z0,c0); - } - - //! Return a shared-memory image referencing a range of channels of the image instance. - /** - \param c0 C-coordinate of the starting channel. - \param c1 C-coordinate of the ending channel. - **/ - CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) { - const unsigned int - beg = (unsigned int)offset(0,0,0,c0), - end = (unsigned int)offset(0,0,0,c1); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_channels(): Invalid request of a shared-memory subset " - "(0->%u,0->%u,0->%u,%u->%u).", - cimg_instance, - _width - 1,_height - 1,_depth - 1,c0,c1); - - return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); - } - - //! Return a shared-memory image referencing a range of channels of the image instance \const. - const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const { - const unsigned int - beg = (unsigned int)offset(0,0,0,c0), - end = (unsigned int)offset(0,0,0,c1); - if (beg>end || beg>=size() || end>=size()) - throw CImgArgumentException(_cimg_instance - "get_shared_channels(): Invalid request of a shared-memory subset " - "(0->%u,0->%u,0->%u,%u->%u).", - cimg_instance, - _width - 1,_height - 1,_depth - 1,c0,c1); - - return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); - } - - //! Return a shared-memory image referencing one channel of the image instance. - /** - \param c0 C-coordinate. - **/ - CImg<T> get_shared_channel(const unsigned int c0) { - return get_shared_channels(c0,c0); - } - - //! Return a shared-memory image referencing one channel of the image instance \const. - const CImg<T> get_shared_channel(const unsigned int c0) const { - return get_shared_channels(c0,c0); - } - - //! Return a shared-memory version of the image instance. - CImg<T> get_shared() { - return CImg<T>(_data,_width,_height,_depth,_spectrum,true); - } - - //! Return a shared-memory version of the image instance \const. - const CImg<T> get_shared() const { - return CImg<T>(_data,_width,_height,_depth,_spectrum,true); - } - - //! Split image into a list along specified axis. - /** - \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param nb Number of splitted parts. - \note - - If \c nb==0, instance image is splitted into blocs of egal values along the specified axis. - - If \c nb<=0, instance image is splitted into blocs of -\c nb pixel wide. - - If \c nb>0, instance image is splitted into \c nb blocs. - **/ - CImgList<T> get_split(const char axis, const int nb=-1) const { - CImgList<T> res; - if (is_empty()) return res; - const char _axis = cimg::lowercase(axis); - - if (nb<0) { // Split by bloc size - const unsigned int dp = (unsigned int)(nb?-nb:1); - switch (_axis) { - case 'x': { - if (_width>dp) { - res.assign(_width/dp + (_width%dp?1:0),1,1); - const unsigned int pe = _width - dp; - cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && - _height*_depth*_spectrum>=128)) - for (unsigned int p = 0; p<pe; p+=dp) - get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); - get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); - } else res.assign(*this); - } break; - case 'y': { - if (_height>dp) { - res.assign(_height/dp + (_height%dp?1:0),1,1); - const unsigned int pe = _height - dp; - cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && - _width*_depth*_spectrum>=128)) - for (unsigned int p = 0; p<pe; p+=dp) - get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); - get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); - } else res.assign(*this); - } break; - case 'z': { - if (_depth>dp) { - res.assign(_depth/dp + (_depth%dp?1:0),1,1); - const unsigned int pe = _depth - dp; - cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && - _width*_height*_spectrum>=128)) - for (unsigned int p = 0; p<pe; p+=dp) - get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]); - get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); - } else res.assign(*this); - } break; - case 'c' : { - if (_spectrum>dp) { - res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1); - const unsigned int pe = _spectrum - dp; - cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && - _width*_height*_depth>=128)) - for (unsigned int p = 0; p<pe; p+=dp) - get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]); - get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); - } else res.assign(*this); - } - } - } else if (nb>0) { // Split by number of (non-homogeneous) blocs - const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0; - if ((unsigned int)nb>siz) - throw CImgArgumentException(_cimg_instance - "get_split(): Instance cannot be split along %c-axis into %u blocs.", - cimg_instance, - axis,nb); - if (nb==1) res.assign(*this); - else { - int err = (int)siz; - unsigned int _p = 0; - switch (_axis) { - case 'x' : { - cimg_forX(*this,p) if ((err-=nb)<=0) { - get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res); - err+=(int)siz; - _p = p + 1U; - } - } break; - case 'y' : { - cimg_forY(*this,p) if ((err-=nb)<=0) { - get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res); - err+=(int)siz; - _p = p + 1U; - } - } break; - case 'z' : { - cimg_forZ(*this,p) if ((err-=nb)<=0) { - get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res); - err+=(int)siz; - _p = p + 1U; - } - } break; - case 'c' : { - cimg_forC(*this,p) if ((err-=nb)<=0) { - get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res); - err+=(int)siz; - _p = p + 1U; - } - } - } - } - } else { // Split by egal values according to specified axis - T current = *_data; - switch (_axis) { - case 'x' : { - int i0 = 0; - cimg_forX(*this,i) - if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); } - get_columns(i0,width() - 1).move_to(res); - } break; - case 'y' : { - int i0 = 0; - cimg_forY(*this,i) - if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); } - get_rows(i0,height() - 1).move_to(res); - } break; - case 'z' : { - int i0 = 0; - cimg_forZ(*this,i) - if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); } - get_slices(i0,depth() - 1).move_to(res); - } break; - case 'c' : { - int i0 = 0; - cimg_forC(*this,i) - if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); } - get_channels(i0,spectrum() - 1).move_to(res); - } break; - default : { - longT i0 = 0; - cimg_foroff(*this,i) - if ((*this)[i]!=current) { - CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); - i0 = (longT)i; current = (*this)[i]; - } - CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res); - } - } - } - return res; - } - - //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis. - /** - \param values Splitting value sequence. - \param axis Axis along which the splitting is performed. Can be '0' to ignore axis. - \param keep_values Tells if the splitting sequence must be kept in the splitted blocs. - **/ - template<typename t> - CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const { - CImgList<T> res; - if (is_empty()) return res; - const ulongT vsiz = values.size(); - const char _axis = cimg::lowercase(axis); - if (!vsiz) return CImgList<T>(*this); - if (vsiz==1) { // Split according to a single value - const T value = (T)*values; - switch (_axis) { - case 'x' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_width && (*this)(i)==value) ++i; - if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; } - while (i<_width && (*this)(i)!=value) ++i; - if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; } - } while (i<_width); - } break; - case 'y' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_height && (*this)(0,i)==value) ++i; - if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; } - while (i<_height && (*this)(0,i)!=value) ++i; - if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; } - } while (i<_height); - } break; - case 'z' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_depth && (*this)(0,0,i)==value) ++i; - if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; } - while (i<_depth && (*this)(0,0,i)!=value) ++i; - if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; } - } while (i<_depth); - } break; - case 'c' : { - unsigned int i0 = 0, i = 0; - do { - while (i<_spectrum && (*this)(0,0,0,i)==value) ++i; - if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; } - while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i; - if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; } - } while (i<_spectrum); - } break; - default : { - const ulongT siz = size(); - ulongT i0 = 0, i = 0; - do { - while (i<siz && (*this)[i]==value) ++i; - if (i>i0) { if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; } - while (i<siz && (*this)[i]!=value) ++i; - if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; } - } while (i<siz); - } - } - } else { // Split according to multiple values - ulongT j = 0; - switch (_axis) { - case 'x' : { - unsigned int i0 = 0, i1 = 0, i = 0; - do { - if ((*this)(i)==*values) { - i1 = i; j = 0; - while (i<_width && (*this)(i)==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_columns(i0,i1 - 1).move_to(res); - if (keep_values) get_columns(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_width); - if (i0<_width) get_columns(i0,width() - 1).move_to(res); - } break; - case 'y' : { - unsigned int i0 = 0, i1 = 0, i = 0; - do { - if ((*this)(0,i)==*values) { - i1 = i; j = 0; - while (i<_height && (*this)(0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_rows(i0,i1 - 1).move_to(res); - if (keep_values) get_rows(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_height); - if (i0<_height) get_rows(i0,height() - 1).move_to(res); - } break; - case 'z' : { - unsigned int i0 = 0, i1 = 0, i = 0; - do { - if ((*this)(0,0,i)==*values) { - i1 = i; j = 0; - while (i<_depth && (*this)(0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_slices(i0,i1 - 1).move_to(res); - if (keep_values) get_slices(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_depth); - if (i0<_depth) get_slices(i0,depth() - 1).move_to(res); - } break; - case 'c' : { - unsigned int i0 = 0, i1 = 0, i = 0; - do { - if ((*this)(0,0,0,i)==*values) { - i1 = i; j = 0; - while (i<_spectrum && (*this)(0,0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) get_channels(i0,i1 - 1).move_to(res); - if (keep_values) get_channels(i1,i - 1).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<_spectrum); - if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res); - } break; - default : { - ulongT i0 = 0, i1 = 0, i = 0; - const ulongT siz = size(); - do { - if ((*this)[i]==*values) { - i1 = i; j = 0; - while (i<siz && (*this)[i]==values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; - if (i>i1) { - if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res); - if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res); - i0 = i; - } else ++i; - } else ++i; - } while (i<siz); - if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res); - } break; - } - } - return res; - } - - //! Append two images along specified axis. - /** - \param img Image to append with instance image. - \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Append alignment in \c [0,1]. - **/ - template<typename t> - CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) { - if (is_empty()) return assign(img,false); - if (!img) return *this; - return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this); - } - - //! Append two images along specified axis \specialization. - CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) { - if (is_empty()) return assign(img,false); - if (!img) return *this; - return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this); - } - - //! Append two images along specified axis \const. - template<typename t> - CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const { - if (is_empty()) return +img; - if (!img) return +*this; - return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align); - } - - //! Append two images along specified axis \specialization. - CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const { - if (is_empty()) return +img; - if (!img) return +*this; - return CImgList<T>(*this,img,true).get_append(axis,align); - } - - //@} - //--------------------------------------- - // - //! \name Filtering / Transforms - //@{ - //--------------------------------------- - - //! Correlate image by a kernel. - /** - \param kernel = the correlation kernel. - \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann) - \param is_normalized = enable local normalization. - \note - - The correlation of the image instance \p *this by the kernel \p kernel is defined to be: - res(x,y,z) = sum_{i,j,k} (*this)(x + i,y + j,z + k)*kernel(i,j,k). - **/ - template<typename t> - CImg<T>& correlate(const CImg<t>& kernel, const bool boundary_conditions=true, - const bool is_normalized=false) { - if (is_empty() || !kernel) return *this; - return get_correlate(kernel,boundary_conditions,is_normalized).move_to(*this); - } - - template<typename t> - CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const bool boundary_conditions=true, - const bool is_normalized=false) const { - return _correlate(kernel,boundary_conditions,is_normalized,false); - } - - //! Correlate image by a kernel \newinstance. - template<typename t> - CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const bool boundary_conditions, - const bool is_normalized, const bool is_convolution) const { - if (is_empty() || !kernel) return *this; - typedef _cimg_Ttfloat Ttfloat; - CImg<Ttfloat> res; - const ulongT - res_whd = (ulongT)_width*_height*_depth, - res_size = res_whd*std::max(_spectrum,kernel._spectrum); - const bool - is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, - is_outer_parallel = res_size>=(cimg_openmp_sizefactor)*32768; - _cimg_abort_init_omp; - cimg_abort_init; - - if (kernel._width==kernel._height && - ((kernel._depth==1 && kernel._width<=6) || (kernel._depth==kernel._width && kernel._width<=3))) { - - // Special optimization done for 2x2, 3x3, 4x4, 5x5, 6x6, 2x2x2 and 3x3x3 kernel. - if (!boundary_conditions && res_whd<=3000*3000) { // Dirichlet boundaries - // For relatively small images, adding a zero border then use optimized NxN convolution loops is faster. - res = (kernel._depth==1?get_crop(-1,-1,_width,_height):get_crop(-1,-1,-1,_width,_height,_depth)). - _correlate(kernel,true,is_normalized,is_convolution); - if (kernel._depth==1) res.crop(1,1,res._width - 2,res._height - 2); - else res.crop(1,1,1,res._width - 2,res._height - 2,res._depth - 2); - - } else { // Neumann boundaries - res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); - cimg::unused(is_inner_parallel,is_outer_parallel); - CImg<t> _kernel; - if (is_convolution) { // Add empty column/row/slice to shift kernel center in case of convolution - const int dw = !(kernel.width()%2), dh = !(kernel.height()%2), dd = !(kernel.depth()%2); - if (dw || dh || dd) - kernel.get_resize(kernel.width() + dw,kernel.height() + dh,kernel.depth() + dd,-100,0,0). - move_to(_kernel); - } - if (!_kernel) _kernel = kernel.get_shared(); - - switch (_kernel._depth) { - case 3 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) - cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg<T> I(27); - Ttfloat *ptrd = res.data(0,0,0,c); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_for3x3x3(img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + - I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + - I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + - I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + - I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + - I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + - I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + - I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + - I[24]*I[24] + I[25]*I[25] + I[26]*I[26]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + - I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] + - I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] + - I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + - I[15]*K[15] + I[16]*K[16] + I[17]*K[17] + - I[18]*K[18] + I[19]*K[19] + I[20]*K[20] + - I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24] + I[25]*K[25] + I[26]*K[26])/std::sqrt(N):0); - } - } else cimg_for3x3x3(img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + - I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] + - I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] + - I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + - I[15]*K[15] + I[16]*K[16] + I[17]*K[17] + - I[18]*K[18] + I[19]*K[19] + I[20]*K[20] + - I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24] + I[25]*K[25] + I[26]*K[26]); - } - } break; - case 2 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) - cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg<T> I(8); - Ttfloat *ptrd = res.data(0,0,0,c); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_for2x2x2(img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + - I[2]*I[2] + I[3]*I[3] + - I[4]*I[4] + I[5]*I[5] + - I[6]*I[6] + I[7]*I[7]); - *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3] + - I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7])/std::sqrt(N):0); - } - } else cimg_for2x2x2(img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3] + - I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7]); - } - } break; - default : - case 1 : - switch (_kernel._width) { - case 6 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) - cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg<T> I(36); - Ttfloat *ptrd = res.data(0,0,0,c); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + - I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + - I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + - I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + - I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] + - I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] + - I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + - I[35]*I[35]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + - I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + - I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] + - I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] + - I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35])/ - std::sqrt(N):0); - } - } else cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + - I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + - I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] + - I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] + - I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35]); - } - } break; - case 5 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) - cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg<T> I(25); - Ttfloat *ptrd = res.data(0,0,0,c); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + - I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + - I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + - I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + - I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + - I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + - I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24])/std::sqrt(N):0); - } - } else cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + - I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + - I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24]); - } - } break; - case 4 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) - cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg<T> I(16); - Ttfloat *ptrd = res.data(0,0,0,c); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + - I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + - I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + - I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15])/ - std::sqrt(N):0); - } - } else cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15]); - } - } break; - case 3 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) - cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg<T> I(9); - Ttfloat *ptrd = res.data(0,0,0,c); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + - I[3]*I[3] + I[4]*I[4] + I[5]*I[5] + - I[6]*I[6] + I[7]*I[7] + I[8]*I[8]); - *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] + - I[3]*K[3] + I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7] + I[8]*K[8])/std::sqrt(N):0); - } - } else cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] + - I[3]*K[3] + I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7] + I[8]*K[8]); - } - } break; - case 2 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) - cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg<T> I(4); - Ttfloat *ptrd = res.data(0,0,0,c); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + - I[2]*I[2] + I[3]*I[3]); - *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3])/std::sqrt(N):0); - } - } else cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3]); - } - } break; - case 1 : - if (is_normalized) res.fill(1); - else cimg_forC(res,c) { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum); - res.get_shared_channel(c).assign(img)*=K[0]; - } - break; - } - } - } - } - - if (!res) { // Generic version for other kernels and boundary conditions - res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); - int - mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, - mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1; - if (is_convolution) cimg::swap(mx1,mx2,my1,my2,mz1,mz2); // Shift kernel center in case of convolution - const int - mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; - cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) - cimg_forC(res,c) _cimg_abort_try_omp { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum); - if (is_normalized) { // Normalized correlation - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) - for (int z = mz1; z<mze; ++z) - for (int y = my1; y<mye; ++y) - for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 { - cimg_abort_test2; - Ttfloat val = 0, N = 0; - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const Ttfloat _val = (Ttfloat)img(x + xm,y + ym,z + zm); - val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm); - N+=_val*_val; - } - N*=M; - res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0); - } _cimg_abort_catch_omp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); - (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Ttfloat val = 0, N = 0; - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const Ttfloat _val = (Ttfloat)img._atXYZ(x + xm,y + ym,z + zm); - val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm); - N+=_val*_val; - } - N*=M; - res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0); - } - } _cimg_abort_catch_omp2 - else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); - (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Ttfloat val = 0, N = 0; - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const Ttfloat _val = (Ttfloat)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); - val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm); - N+=_val*_val; - } - N*=M; - res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0); - } - } _cimg_abort_catch_omp2 - } else { // Classical correlation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) - for (int z = mz1; z<mze; ++z) - for (int y = my1; y<mye; ++y) - for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 { - cimg_abort_test2; - Ttfloat val = 0; - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - val+=img(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm); - res(x,y,z,c) = (Ttfloat)val; - } _cimg_abort_catch_omp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); - (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Ttfloat val = 0; - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - val+=img._atXYZ(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm); - res(x,y,z,c) = (Ttfloat)val; - } - } _cimg_abort_catch_omp2 - else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); - (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Ttfloat val = 0; - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - val+=img.atXYZ(x + xm,y + ym,z + zm,0,(T)0)*K(mx1 + xm,my1 + ym,mz1 + zm); - res(x,y,z,c) = (Ttfloat)val; - } - } _cimg_abort_catch_omp2 - } - } _cimg_abort_catch_omp - } - cimg_abort_test; - return res; - } - - //! Convolve image by a kernel. - /** - \param kernel = the correlation kernel. - \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann) - \param is_normalized = enable local normalization. - \note - - The result \p res of the convolution of an image \p img by a kernel \p kernel is defined to be: - res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*kernel(i,j,k) - **/ - template<typename t> - CImg<T>& convolve(const CImg<t>& kernel, const bool boundary_conditions=true, const bool is_normalized=false) { - if (is_empty() || !kernel) return *this; - return get_convolve(kernel,boundary_conditions,is_normalized).move_to(*this); - } - - //! Convolve image by a kernel \newinstance. - template<typename t> - CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const bool boundary_conditions=true, - const bool is_normalized=false) const { - return _correlate(CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true). - get_mirror('x').resize(kernel,-1),boundary_conditions,is_normalized,true); - } - - //! Cumulate image values, optionally along specified axis. - /** - \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account. - **/ - CImg<T>& cumulate(const char axis=0) { - switch (cimg::lowercase(axis)) { - case 'x' : - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth*_spectrum>=16)) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - Tlong cumul = (Tlong)0; - cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; } - } - break; - case 'y' : { - const ulongT w = (ulongT)_width; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && - _width*_depth*_spectrum>=16)) - cimg_forXZC(*this,x,z,c) { - T *ptrd = data(x,0,z,c); - Tlong cumul = (Tlong)0; - cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; } - } - } break; - case 'z' : { - const ulongT wh = (ulongT)_width*_height; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && - _width*_depth*_spectrum>=16)) - cimg_forXYC(*this,x,y,c) { - T *ptrd = data(x,y,0,c); - Tlong cumul = (Tlong)0; - cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; } - } - } break; - case 'c' : { - const ulongT whd = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && - _width*_height*_depth>=16)) - cimg_forXYZ(*this,x,y,z) { - T *ptrd = data(x,y,z,0); - Tlong cumul = (Tlong)0; - cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; } - } - } break; - default : { // Global cumulation - Tlong cumul = (Tlong)0; - cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; } - } - } - return *this; - } - - //! Cumulate image values, optionally along specified axis \newinstance. - CImg<Tlong> get_cumulate(const char axis=0) const { - return CImg<Tlong>(*this,false).cumulate(axis); - } - - //! Cumulate image values, along specified axes. - /** - \param axes Cumulation axes, as a C-string. - \note \c axes may contains multiple characters, e.g. \c "xyz" - **/ - CImg<T>& cumulate(const char *const axes) { - for (const char *s = axes; *s; ++s) cumulate(*s); - return *this; - } - - //! Cumulate image values, along specified axes \newinstance. - CImg<Tlong> get_cumulate(const char *const axes) const { - return CImg<Tlong>(*this,false).cumulate(axes); - } - - //! Erode image by a structuring element. - /** - \param kernel Structuring element. - \param boundary_conditions Boundary conditions. - \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). - **/ - template<typename t> - CImg<T>& erode(const CImg<t>& kernel, const bool boundary_conditions=true, - const bool is_real=false) { - if (is_empty() || !kernel) return *this; - return get_erode(kernel,boundary_conditions,is_real).move_to(*this); - } - - //! Erode image by a structuring element \newinstance. - template<typename t> - CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const bool boundary_conditions=true, - const bool is_real=false) const { - if (is_empty() || !kernel) return *this; - if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0); - typedef _cimg_Tt Tt; - CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); - const int - mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, - mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1, - mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; - const bool - is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, - is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; - cimg::unused(is_inner_parallel,is_outer_parallel); - _cimg_abort_init_omp; - cimg_abort_init; - cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) - cimg_forC(res,c) _cimg_abort_try_omp { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum); - if (is_real) { // Real erosion - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) - for (int z = mz1; z<mze; ++z) - for (int y = my1; y<mye; ++y) - for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 { - cimg_abort_test2; - Tt min_val = cimg::type<Tt>::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval); - if (cval<min_val) min_val = cval; - } - res(x,y,z,c) = min_val; - } _cimg_abort_catch_omp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type<Tt>::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); - if (cval<min_val) min_val = cval; - } - res(x,y,z,c) = min_val; - } - } _cimg_abort_catch_omp2 - else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type<Tt>::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); - if (cval<min_val) min_val = cval; - } - res(x,y,z,c) = min_val; - } - } _cimg_abort_catch_omp2 - - } else { // Binary erosion - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) - for (int z = mz1; z<mze; ++z) - for (int y = my1; y<mye; ++y) - for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 { - cimg_abort_test2; - Tt min_val = cimg::type<Tt>::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const Tt cval = (Tt)img(x + xm,y + ym,z + zm); - if (cval<min_val) min_val = cval; - } - res(x,y,z,c) = min_val; - } _cimg_abort_catch_omp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type<Tt>::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); - if (cval<min_val) min_val = cval; - } - res(x,y,z,c) = min_val; - } - } _cimg_abort_catch_omp2 - else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type<Tt>::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); - if (cval<min_val) min_val = cval; - } - res(x,y,z,c) = min_val; - } - } _cimg_abort_catch_omp2 - } - } _cimg_abort_catch_omp - cimg_abort_test; - return res; - } - - //! Erode image by a rectangular structuring element of specified size. - /** - \param sx Width of the structuring element. - \param sy Height of the structuring element. - \param sz Depth of the structuring element. - **/ - CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { - if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; - if (sx>1 && _width>1) { // Along X-axis - const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; - CImg<T> buf(L); - cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288)) - cimg_forYZC(*this,y,z,c) { - T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - T cur = *ptrs; ptrs+=off; bool is_first = true; - for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { - const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} - *(ptrd++) = cur; - if (ptrs>=ptrse) { - T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - for (int p = s1; p>0 && ptrd<=ptrde; --p) { - const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } - *(ptrd++) = cur; - } - for (int p = L - s - 1; p>0; --p) { - const T val = *ptrs; ptrs+=off; - if (is_first) { - const T *nptrs = ptrs - off; cur = val; - for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; } - nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false; - } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } - *(ptrd++) = cur; - } - ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; - for (int p = s1; p>0 && ptrs>=ptrsb; --p) { - const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; - } - *(ptrd--) = cur; - for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { - const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; - } - T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } - } - } - } - - if (sy>1 && _height>1) { // Along Y-axis - const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, - s2 = _s2>L?L:_s2; - CImg<T> buf(L); - cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288)) - cimg_forXZC(*this,x,z,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - T cur = *ptrs; ptrs+=off; bool is_first = true; - for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { - const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } - } - *(ptrd++) = cur; - if (ptrs>=ptrse) { - T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - for (int p = s1; p>0 && ptrd<=ptrde; --p) { - const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } - *(ptrd++) = cur; - } - for (int p = L - s - 1; p>0; --p) { - const T val = *ptrs; ptrs+=off; - if (is_first) { - const T *nptrs = ptrs - off; cur = val; - for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; } - nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false; - } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } - *(ptrd++) = cur; - } - ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; - for (int p = s1; p>0 && ptrs>=ptrsb; --p) { - const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; - } - *(ptrd--) = cur; - for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { - const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; - } - T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } - } - } - } - - if (sz>1 && _depth>1) { // Along Z-axis - const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, - s2 = _s2>L?L:_s2; - CImg<T> buf(L); - cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288)) - cimg_forXYC(*this,x,y,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - T cur = *ptrs; ptrs+=off; bool is_first = true; - for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { - const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } - } - *(ptrd++) = cur; - if (ptrs>=ptrse) { - T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - for (int p = s1; p>0 && ptrd<=ptrde; --p) { - const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } - *(ptrd++) = cur; - } - for (int p = L - s - 1; p>0; --p) { - const T val = *ptrs; ptrs+=off; - if (is_first) { - const T *nptrs = ptrs - off; cur = val; - for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; } - nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false; - } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } - *(ptrd++) = cur; - } - ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; - for (int p = s1; p>0 && ptrs>=ptrsb; --p) { - const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; - } - *(ptrd--) = cur; - for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { - const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; - } - T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } - } - } - } - return *this; - } - - //! Erode image by a rectangular structuring element of specified size \newinstance. - CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { - return (+*this).erode(sx,sy,sz); - } - - //! Erode the image by a square structuring element of specified size. - /** - \param s Size of the structuring element. - **/ - CImg<T>& erode(const unsigned int s) { - return erode(s,s,s); - } - - //! Erode the image by a square structuring element of specified size \newinstance. - CImg<T> get_erode(const unsigned int s) const { - return (+*this).erode(s); - } - - //! Dilate image by a structuring element. - /** - \param kernel Structuring element. - \param boundary_conditions Boundary conditions. - \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). - **/ - template<typename t> - CImg<T>& dilate(const CImg<t>& kernel, const bool boundary_conditions=true, - const bool is_real=false) { - if (is_empty() || !kernel) return *this; - return get_dilate(kernel,boundary_conditions,is_real).move_to(*this); - } - - //! Dilate image by a structuring element \newinstance. - template<typename t> - CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const bool boundary_conditions=true, - const bool is_real=false) const { - if (is_empty() || !kernel || (!is_real && kernel==0)) return *this; - typedef _cimg_Tt Tt; - CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); - const int - mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2, - mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1, - mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; - const bool - is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, - is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; - cimg::unused(is_inner_parallel,is_outer_parallel); - _cimg_abort_init_omp; - cimg_abort_init; - cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) - cimg_forC(res,c) _cimg_abort_try_omp { - cimg_abort_test; - const CImg<T> img = get_shared_channel(c%_spectrum); - const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum); - if (is_real) { // Real dilation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) - for (int z = mz1; z<mze; ++z) - for (int y = my1; y<mye; ++y) - for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 { - cimg_abort_test2; - Tt max_val = cimg::type<Tt>::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval); - if (cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; - } _cimg_abort_catch_omp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type<Tt>::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); - if (cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; - } - } _cimg_abort_catch_omp2 - else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type<Tt>::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); - if (cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; - } - } _cimg_abort_catch_omp2 - } else { // Binary dilation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) - for (int z = mz1; z<mze; ++z) - for (int y = my1; y<mye; ++y) - for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 { - cimg_abort_test2; - Tt max_val = cimg::type<Tt>::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const Tt cval = (Tt)img(x + xm,y + ym,z + zm); - if (cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; - } _cimg_abort_catch_omp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type<Tt>::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); - if (cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; - } - } _cimg_abort_catch_omp2 - else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type<Tt>::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); - if (cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; - } - } _cimg_abort_catch_omp2 - } - } _cimg_abort_catch_omp - cimg_abort_test; - return res; - } - - //! Dilate image by a rectangular structuring element of specified size. - /** - \param sx Width of the structuring element. - \param sy Height of the structuring element. - \param sz Depth of the structuring element. - **/ - CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { - if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; - if (sx>1 && _width>1) { // Along X-axis - const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; - CImg<T> buf(L); - cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288)) - cimg_forYZC(*this,y,z,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - T cur = *ptrs; ptrs+=off; bool is_first = true; - for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { - const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } - } - *(ptrd++) = cur; - if (ptrs>=ptrse) { - T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - for (int p = s1; p>0 && ptrd<=ptrde; --p) { - const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } - *(ptrd++) = cur; - } - for (int p = L - s - 1; p>0; --p) { - const T val = *ptrs; ptrs+=off; - if (is_first) { - const T *nptrs = ptrs - off; cur = val; - for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } - nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; - } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } - *(ptrd++) = cur; - } - ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; - for (int p = s1; p>0 && ptrs>=ptrsb; --p) { - const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; - } - *(ptrd--) = cur; - for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { - const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; - } - T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } - } - } - } - - if (sy>1 && _height>1) { // Along Y-axis - const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, - s2 = _s2>L?L:_s2; - CImg<T> buf(L); - cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288)) - cimg_forXZC(*this,x,z,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - T cur = *ptrs; ptrs+=off; bool is_first = true; - for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { - const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } - } - *(ptrd++) = cur; - if (ptrs>=ptrse) { - T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - for (int p = s1; p>0 && ptrd<=ptrde; --p) { - const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } - *(ptrd++) = cur; - } - for (int p = L - s - 1; p>0; --p) { - const T val = *ptrs; ptrs+=off; - if (is_first) { - const T *nptrs = ptrs - off; cur = val; - for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } - nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; - } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } - *(ptrd++) = cur; - } - ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; - for (int p = s1; p>0 && ptrs>=ptrsb; --p) { - const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; - } - *(ptrd--) = cur; - for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { - const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; - } - T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } - } - } - } - - if (sz>1 && _depth>1) { // Along Z-axis - const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, - s2 = _s2>L?L:_s2; - CImg<T> buf(L); - cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288)) - cimg_forXYC(*this,x,y,c) { - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - T cur = *ptrs; ptrs+=off; bool is_first = true; - for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { - const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } - } - *(ptrd++) = cur; - if (ptrs>=ptrse) { - T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; } - } else { - for (int p = s1; p>0 && ptrd<=ptrde; --p) { - const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } - *(ptrd++) = cur; - } - for (int p = L - s - 1; p>0; --p) { - const T val = *ptrs; ptrs+=off; - if (is_first) { - const T *nptrs = ptrs - off; cur = val; - for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } - nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; - } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } - *(ptrd++) = cur; - } - ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; - for (int p = s1; p>0 && ptrs>=ptrsb; --p) { - const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; - } - *(ptrd--) = cur; - for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { - const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; - } - T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } - } - } - } - return *this; - } - - //! Dilate image by a rectangular structuring element of specified size \newinstance. - CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { - return (+*this).dilate(sx,sy,sz); - } - - //! Dilate image by a square structuring element of specified size. - /** - \param s Size of the structuring element. - **/ - CImg<T>& dilate(const unsigned int s) { - return dilate(s,s,s); - } - - //! Dilate image by a square structuring element of specified size \newinstance. - CImg<T> get_dilate(const unsigned int s) const { - return (+*this).dilate(s); - } - - //! Compute watershed transform. - /** - \param priority Priority map. - \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity - in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case. - \note Non-zero values of the instance instance are propagated to zero-valued ones according to - specified the priority map. - **/ - template<typename t> - CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) { -#define _cimg_watershed_init(cond,X,Y,Z) \ - if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds) - -#define _cimg_watershed_propagate(cond,X,Y,Z) \ - if (cond) { \ - if ((*this)(X,Y,Z)) { \ - ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \ - d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \ - if (d<dmin) { dmin = d; nmin = ns; label = (*this)(xs,ys,zs); } \ - } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \ - } - - if (is_empty()) return *this; - if (!is_sameXYZ(priority)) - throw CImgArgumentException(_cimg_instance - "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) " - "have different dimensions.", - cimg_instance, - priority._width,priority._height,priority._depth,priority._spectrum,priority._data); - if (_spectrum!=1) { - cimg_forC(*this,c) - get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum)); - return *this; - } - - CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3); - CImg<typename cimg::superset2<T,t,int>::type> Q; - unsigned int sizeQ = 0; - int px, nx, py, ny, pz, nz; - bool is_px, is_nx, is_py, is_ny, is_pz, is_nz; - const bool is_3d = _depth>1; - - // Find seed points and insert them in priority queue. - unsigned int nb_seeds = 0; - const T *ptrs = _data; - cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version - if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0); - seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z; - px = x - 1; nx = x + 1; - py = y - 1; ny = y + 1; - pz = z - 1; nz = z + 1; - is_px = px>=0; is_nx = nx<width(); - is_py = py>=0; is_ny = ny<height(); - is_pz = pz>=0; is_nz = nz<depth(); - _cimg_watershed_init(is_px,px,y,z); - _cimg_watershed_init(is_nx,nx,y,z); - _cimg_watershed_init(is_py,x,py,z); - _cimg_watershed_init(is_ny,x,ny,z); - if (is_3d) { - _cimg_watershed_init(is_pz,x,y,pz); - _cimg_watershed_init(is_nz,x,y,nz); - } - if (is_high_connectivity) { - _cimg_watershed_init(is_px && is_py,px,py,z); - _cimg_watershed_init(is_nx && is_py,nx,py,z); - _cimg_watershed_init(is_px && is_ny,px,ny,z); - _cimg_watershed_init(is_nx && is_ny,nx,ny,z); - if (is_3d) { - _cimg_watershed_init(is_px && is_pz,px,y,pz); - _cimg_watershed_init(is_nx && is_pz,nx,y,pz); - _cimg_watershed_init(is_px && is_nz,px,y,nz); - _cimg_watershed_init(is_nx && is_nz,nx,y,nz); - _cimg_watershed_init(is_py && is_pz,x,py,pz); - _cimg_watershed_init(is_ny && is_pz,x,ny,pz); - _cimg_watershed_init(is_py && is_nz,x,py,nz); - _cimg_watershed_init(is_ny && is_nz,x,ny,nz); - _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz); - _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz); - _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz); - _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz); - _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz); - _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz); - _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz); - _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz); - } - } - labels(x,y,z) = nb_seeds; - } - - // Start watershed computation. - while (sizeQ) { - - // Get and remove point with maximal priority from the queue. - const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); - const unsigned int n = labels(x,y,z); - px = x - 1; nx = x + 1; - py = y - 1; ny = y + 1; - pz = z - 1; nz = z + 1; - is_px = px>=0; is_nx = nx<width(); - is_py = py>=0; is_ny = ny<height(); - is_pz = pz>=0; is_nz = nz<depth(); - - // Check labels of the neighbors. - Q._priority_queue_remove(sizeQ); - - unsigned int xs, ys, zs, ns, nmin = 0; - float d, dmin = cimg::type<float>::inf(); - T label = (T)0; - _cimg_watershed_propagate(is_px,px,y,z); - _cimg_watershed_propagate(is_nx,nx,y,z); - _cimg_watershed_propagate(is_py,x,py,z); - _cimg_watershed_propagate(is_ny,x,ny,z); - if (is_3d) { - _cimg_watershed_propagate(is_pz,x,y,pz); - _cimg_watershed_propagate(is_nz,x,y,nz); - } - if (is_high_connectivity) { - _cimg_watershed_propagate(is_px && is_py,px,py,z); - _cimg_watershed_propagate(is_nx && is_py,nx,py,z); - _cimg_watershed_propagate(is_px && is_ny,px,ny,z); - _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z); - if (is_3d) { - _cimg_watershed_propagate(is_px && is_pz,px,y,pz); - _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz); - _cimg_watershed_propagate(is_px && is_nz,px,y,nz); - _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz); - _cimg_watershed_propagate(is_py && is_pz,x,py,pz); - _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz); - _cimg_watershed_propagate(is_py && is_nz,x,py,nz); - _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz); - _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz); - _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz); - _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz); - _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz); - _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz); - _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz); - _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz); - _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz); - } - } - (*this)(x,y,z) = label; - labels(x,y,z) = ++nmin; - } - return *this; - } - - //! Compute watershed transform \newinstance. - template<typename t> - CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const { - return (+*this).watershed(priority,is_high_connectivity); - } - - // [internal] Insert/Remove items in priority queue, for watershed/distance transforms. - template<typename tq, typename tv> - bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value, - const unsigned int x, const unsigned int y, const unsigned int z, - const unsigned int n=1) { - if (is_queued(x,y,z)) return false; - is_queued(x,y,z) = (tq)n; - if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } - (*this)(siz - 1,0) = (T)value; - (*this)(siz - 1,1) = (T)x; - (*this)(siz - 1,2) = (T)y; - (*this)(siz - 1,3) = (T)z; - for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos + 1)/2 - 1,0); pos = par) { - cimg::swap((*this)(pos,0),(*this)(par,0)); - cimg::swap((*this)(pos,1),(*this)(par,1)); - cimg::swap((*this)(pos,2),(*this)(par,2)); - cimg::swap((*this)(pos,3),(*this)(par,3)); - } - return true; - } - - CImg<T>& _priority_queue_remove(unsigned int& siz) { - (*this)(0,0) = (*this)(--siz,0); - (*this)(0,1) = (*this)(siz,1); - (*this)(0,2) = (*this)(siz,2); - (*this)(0,3) = (*this)(siz,3); - const float value = (*this)(0,0); - for (unsigned int pos = 0, left = 0, right = 0; - ((right=2*(pos + 1),(left=right - 1))<siz && value<(*this)(left,0)) || - (right<siz && value<(*this)(right,0));) { - if (right<siz) { - if ((*this)(left,0)>(*this)(right,0)) { - cimg::swap((*this)(pos,0),(*this)(left,0)); - cimg::swap((*this)(pos,1),(*this)(left,1)); - cimg::swap((*this)(pos,2),(*this)(left,2)); - cimg::swap((*this)(pos,3),(*this)(left,3)); - pos = left; - } else { - cimg::swap((*this)(pos,0),(*this)(right,0)); - cimg::swap((*this)(pos,1),(*this)(right,1)); - cimg::swap((*this)(pos,2),(*this)(right,2)); - cimg::swap((*this)(pos,3),(*this)(right,3)); - pos = right; - } - } else { - cimg::swap((*this)(pos,0),(*this)(left,0)); - cimg::swap((*this)(pos,1),(*this)(left,1)); - cimg::swap((*this)(pos,2),(*this)(left,2)); - cimg::swap((*this)(pos,3),(*this)(left,3)); - pos = left; - } - } - return *this; - } - - //! Apply recursive Deriche filter. - /** - \param sigma Standard deviation of the filter. - \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>. - \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>. - **/ - CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x', - const bool boundary_conditions=true) { -#define _cimg_deriche_apply \ - CImg<Tfloat> Y(N); \ - Tfloat *ptrY = Y._data, yb = 0, yp = 0; \ - T xp = (T)0; \ - if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \ - for (int m = 0; m<N; ++m) { \ - const T xc = *ptrX; ptrX+=off; \ - const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \ - xp = xc; yb = yp; yp = yc; \ - } \ - T xn = (T)0, xa = (T)0; \ - Tfloat yn = 0, ya = 0; \ - if (boundary_conditions) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \ - for (int n = N - 1; n>=0; --n) { \ - const T xc = *(ptrX-=off); \ - const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \ - xa = xn; xn = xc; ya = yn; yn = yc; \ - *ptrX = (T)(*(--ptrY)+yc); \ - } - const char naxis = cimg::lowercase(axis); - const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; - if (is_empty() || (nsigma<0.1f && !order)) return *this; - const float - nnsigma = nsigma<0.1f?0.1f:nsigma, - alpha = 1.695f/nnsigma, - ema = (float)std::exp(-alpha), - ema2 = (float)std::exp(-2*alpha), - b1 = -2*ema, - b2 = ema2; - float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; - switch (order) { - case 0 : { - const float k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2); - a0 = k; - a1 = k*(alpha - 1)*ema; - a2 = k*(alpha + 1)*ema; - a3 = -k*ema2; - } break; - case 1 : { - const float k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema); - a0 = a3 = 0; - a1 = k*ema; - a2 = -a1; - } break; - case 2 : { - const float - ea = (float)std::exp(-alpha), - k = -(ema2 - 1)/(2*alpha*ema), - kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea)); - a0 = kn; - a1 = -kn*(1 + k*alpha)*ema; - a2 = kn*(1 - k*alpha)*ema; - a3 = -kn*ema2; - } break; - default : - throw CImgArgumentException(_cimg_instance - "deriche(): Invalid specified filter order %u " - "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", - cimg_instance, - order); - } - coefp = (a0 + a1)/(1 + b1 + b2); - coefn = (a2 + a3)/(1 + b1 + b2); - switch (naxis) { - case 'x' : { - const int N = width(); - const ulongT off = 1U; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; } - } break; - case 'y' : { - const int N = height(); - const ulongT off = (ulongT)_width; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; } - } break; - case 'z' : { - const int N = depth(); - const ulongT off = (ulongT)_width*_height; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; } - } break; - default : { - const int N = spectrum(); - const ulongT off = (ulongT)_width*_height*_depth; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; } - } - } - return *this; - } - - //! Apply recursive Deriche filter \newinstance. - CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x', - const bool boundary_conditions=true) const { - return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions); - } - - // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()). - /* - \param ptr the pointer of the data - \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3]. - \param N size of the data - \param off the offset between two data point - \param order the order of the filter 0 (smoothing), 1st derivtive, 2nd derivative, 3rd derivative - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>. - \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005). - */ - static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off, - const unsigned int order, const bool boundary_conditions) { - double val[4] = { 0 }; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..] - const double - sumsq = filter[0], sum = sumsq * sumsq, - a1 = filter[1], a2 = filter[2], a3 = filter[3], - scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) ); - double M[9]; // Triggs matrix - M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2); - M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1); - M[2] = scaleM * a3 * (a1 + a3 * a2); - M[3] = scaleM * (a1 + a3 * a2); - M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1); - M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.); - M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2); - M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3); - M[8] = scaleM * a3 * (a1 + a3 * a2); - switch (order) { - case 0 : { - const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0); - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0); - } else { - // apply Triggs boundary conditions - const double - uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3), - unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n<N; ++n) { - val[0] = (*data); - if (pass) val[0] *= sum; - for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k]; - *data = (T)val[0]; - if (!pass) data += off; else data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - if (!pass) data -= off; - } - } break; - case 1 : { - double x[3]; // [front,center,back] - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - // apply Triggs boundary conditions - const double - unp = val[1], unp1 = val[2], unp2 = val[3]; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n<N - 1; ++n) { - if (!pass) { - x[0] = *(data + off); - val[0] = 0.5f * (x[0] - x[2]); - } else val[0] = (*data) * sum; - for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k]; - *data = (T)val[0]; - if (!pass) { - data += off; - for (int k = 2; k>0; --k) x[k] = x[k - 1]; - } else { data-=off;} - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - *data = (T)0; - } - } break; - case 2: { - double x[3]; // [front,center,back] - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - // apply Triggs boundary conditions - const double - unp = val[1], unp1 = val[2], unp2 = val[3]; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n<N - 1; ++n) { - if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); } - else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; } - for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k]; - *data = (T)val[0]; - if (!pass) data += off; else data -= off; - for (int k = 2; k>0; --k) x[k] = x[k - 1]; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - *data = (T)0; - } - } break; - case 3: { - double x[3]; // [front,center,back] - for (int pass = 0; pass<2; ++pass) { - if (!pass) { - for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - // apply Triggs boundary conditions - const double - unp = val[1], unp1 = val[2], unp2 = val[3]; - val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; - val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; - val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; - *data = (T)val[0]; - data -= off; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - for (int n = pass; n<N - 1; ++n) { - if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); } - else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; } - for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k]; - *data = (T)val[0]; - if (!pass) data += off; else data -= off; - for (int k = 2; k>0; --k) x[k] = x[k - 1]; - for (int k = 3; k>0; --k) val[k] = val[k - 1]; - } - *data = (T)0; - } - } break; - } - } - - //! Van Vliet recursive Gaussian filter. - /** - \param sigma standard deviation of the Gaussian filter - \param order the order of the filter 0,1,2,3 - \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>. - \note dirichlet boundary condition has a strange behavior - - I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering. - IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002. - - (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995) - - Boundary conditions (only for order 0) using Triggs matrix, from - B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet - recursive filtering. IEEE Trans. Signal Processing, - vol. 54, pp. 2365-2367, 2006. - **/ - CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x', - const bool boundary_conditions=true) { - if (is_empty()) return *this; - if (!cimg::type<T>::is_float()) - return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this); - const char naxis = cimg::lowercase(axis); - const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; - if (is_empty() || (nsigma<0.5f && !order)) return *this; - const double - nnsigma = nsigma<0.5f?0.5f:nsigma, - m0 = 1.16680, m1 = 1.10783, m2 = 1.40586, - m1sq = m1 * m1, m2sq = m2 * m2, - q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)), - qsq = q * q, - scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq), - b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale, - b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale, - b3 = -qsq * q / scale, - B = ( m0 * (m1sq + m2sq) ) / scale; - double filter[4]; - filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3; - switch (naxis) { - case 'x' : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forYZC(*this,y,z,c) - _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); - } break; - case 'y' : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXZC(*this,x,z,c) - _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions); - } break; - case 'z' : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXYC(*this,x,y,c) - _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height, - order,boundary_conditions); - } break; - default : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXYZ(*this,x,y,z) - _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth, - order,boundary_conditions); - } - } - return *this; - } - - //! Blur image using Van Vliet recursive Gaussian filter. \newinstance. - CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x', - const bool boundary_conditions=true) const { - return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions); - } - - //! Blur image. - /** - \param sigma_x Standard deviation of the blur, along the X-axis. - \param sigma_y Standard deviation of the blur, along the Y-axis. - \param sigma_z Standard deviation of the blur, along the Z-axis. - \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>. - \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel. - \note - - The blur is computed as a 0-order Deriche filter. This is not a gaussian blur. - - This is a recursive algorithm, not depending on the values of the standard deviations. - \see deriche(), vanvliet(). - **/ - CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z, - const bool boundary_conditions=true, const bool is_gaussian=false) { - if (is_empty()) return *this; - if (is_gaussian) { - if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions); - if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions); - if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions); - } else { - if (_width>1) deriche(sigma_x,0,'x',boundary_conditions); - if (_height>1) deriche(sigma_y,0,'y',boundary_conditions); - if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions); - } - return *this; - } - - //! Blur image \newinstance. - CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z, - const bool boundary_conditions=true, const bool is_gaussian=false) const { - return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian); - } - - //! Blur image isotropically. - /** - \param sigma Standard deviation of the blur. - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a - \param is_gaussian Use a gaussian kernel (VanVliet) is set, a pseudo-gaussian (Deriche) otherwise. - \see deriche(), vanvliet(). - **/ - CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) { - const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; - return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian); - } - - //! Blur image isotropically \newinstance. - CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const { - return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian); - } - - //! Blur image anisotropically, directed by a field of diffusion tensors. - /** - \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing. - \param amplitude Amplitude of the smoothing. - \param dl Spatial discretization. - \param da Angular discretization. - \param gauss_prec Precision of the diffusion process. - \param interpolation_type Interpolation scheme. - Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>. - \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. - **/ - template<typename t> - CImg<T>& blur_anisotropic(const CImg<t>& G, - const float amplitude=60, const float dl=0.8f, const float da=30, - const float gauss_prec=2, const unsigned int interpolation_type=0, - const bool is_fast_approx=1) { - - // Check arguments and init variables - if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6)) - throw CImgArgumentException(_cimg_instance - "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).", - cimg_instance, - G._width,G._height,G._depth,G._spectrum,G._data); - if (is_empty() || dl<0) return *this; - const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100; - unsigned int iamplitude = cimg::round(namplitude); - const bool is_3d = (G._spectrum==6); - T val_min, val_max = max_min(val_min); - _cimg_abort_init_omp; - cimg_abort_init; - - if (da<=0) { // Iterated oriented Laplacians - CImg<Tfloat> velocity(_width,_height,_depth,_spectrum); - for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) { - Tfloat *ptrd = velocity._data, veloc_max = 0; - if (is_3d) // 3D version - cimg_forC(*this,c) { - cimg_abort_test; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - const Tfloat - ixx = Incc + Ipcc - 2*Iccc, - ixy = (Innc + Ippc - Inpc - Ipnc)/4, - ixz = (Incn + Ipcp - Incp - Ipcn)/4, - iyy = Icnc + Icpc - 2*Iccc, - iyz = (Icnn + Icpp - Icnp - Icpn)/4, - izz = Iccn + Iccp - 2*Iccc, - veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz + - G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz); - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } - } - else // 2D version - cimg_forZC(*this,z,c) { - cimg_abort_test; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - const Tfloat - ixx = Inc + Ipc - 2*Icc, - ixy = (Inn + Ipp - Inp - Ipn)/4, - iyy = Icn + Icp - 2*Icc, - veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy); - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } - } - if (veloc_max>0) *this+=(velocity*=dl/veloc_max); - } - } else { // LIC-based smoothing - const ulongT whd = (ulongT)_width*_height*_depth; - const float sqrt2amplitude = (float)std::sqrt(2*namplitude); - const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1; - CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0); - int N = 0; - if (is_3d) { // 3D version - for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) { - const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), - da2 = datmp<1?360.f:datmp; - for (float theta = 0; theta<360; (theta+=da2),++N) { - const float - thetar = (float)(theta*cimg::PI/180), - vx = (float)(std::cos(thetar)*std::cos(phir)), - vy = (float)(std::sin(thetar)*std::cos(phir)), - vz = (float)std::sin(phir); - const t - *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2), - *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5); - Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3); - cimg_forXYZ(G,xg,yg,zg) { - const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++); - const float - u = (float)(a*vx + b*vy + c*vz), - v = (float)(b*vx + d*vy + e*vz), - w = (float)(c*vx + e*vy + f*vz), - n = 1e-5f + cimg::hypot(u,v,w), - dln = dl/n; - *(pd0++) = (Tfloat)(u*dln); - *(pd1++) = (Tfloat)(v*dln); - *(pd2++) = (Tfloat)(w*dln); - *(pd3++) = (Tfloat)n; - } - - cimg_abort_test; - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth>=2) - firstprivate(val)) - cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 { - cimg_abort_test2; - cimg_forX(*this,x) { - val.fill(0); - const float - n = (float)W(x,y,z,3), - fsigma = (float)(n*sqrt2amplitude), - fsigma2 = 2*fsigma*fsigma, - length = gauss_prec*fsigma; - float - S = 0, - X = (float)x, - Y = (float)y, - Z = (float)z; - switch (interpolation_type) { - case 0 : { // Nearest neighbor - for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { - const int - cx = (int)(X + 0.5f), - cy = (int)(Y + 0.5f), - cz = (int)(Z + 0.5f); - const float - u = (float)W(cx,cy,cz,0), - v = (float)W(cx,cy,cz,1), - w = (float)W(cx,cy,cz,2); - if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; } - else { - const float coef = (float)std::exp(-l*l/fsigma2); - cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c)); - S+=coef; - } - X+=u; Y+=v; Z+=w; - } - } break; - case 1 : { // Linear interpolation - for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { - const float - u = (float)(W._linear_atXYZ(X,Y,Z,0)), - v = (float)(W._linear_atXYZ(X,Y,Z,1)), - w = (float)(W._linear_atXYZ(X,Y,Z,2)); - if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } - else { - const float coef = (float)std::exp(-l*l/fsigma2); - cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); - S+=coef; - } - X+=u; Y+=v; Z+=w; - } - } break; - default : { // 2nd order Runge Kutta - for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { - const float - u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)), - v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)), - w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)), - u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)), - v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)), - w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2)); - if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } - else { - const float coef = (float)std::exp(-l*l/fsigma2); - cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); - S+=coef; - } - X+=u; Y+=v; Z+=w; - } - } break; - } - Tfloat *ptrd = res.data(x,y,z); - if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } - else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; } - } - } _cimg_abort_catch_omp2 - } - } - } else { // 2D LIC algorithm - for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) { - const float thetar = (float)(theta*cimg::PI/180), - vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar)); - const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2); - Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2); - cimg_forXY(G,xg,yg) { - const t a = *(pa++), b = *(pb++), c = *(pc++); - const float - u = (float)(a*vx + b*vy), - v = (float)(b*vx + c*vy), - n = std::max(1e-5f,cimg::hypot(u,v)), - dln = dl/n; - *(pd0++) = (Tfloat)(u*dln); - *(pd1++) = (Tfloat)(v*dln); - *(pd2++) = (Tfloat)n; - } - - cimg_abort_test; - cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2) - firstprivate(val)) - cimg_forY(*this,y) _cimg_abort_try_omp2 { - cimg_abort_test2; - cimg_forX(*this,x) { - val.fill(0); - const float - n = (float)W(x,y,0,2), - fsigma = (float)(n*sqrt2amplitude), - fsigma2 = 2*fsigma*fsigma, - length = gauss_prec*fsigma; - float - S = 0, - X = (float)x, - Y = (float)y; - switch (interpolation_type) { - case 0 : { // Nearest-neighbor - for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { - const int - cx = (int)(X + 0.5f), - cy = (int)(Y + 0.5f); - const float - u = (float)W(cx,cy,0,0), - v = (float)W(cx,cy,0,1); - if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; } - else { - const float coef = (float)std::exp(-l*l/fsigma2); - cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c)); - S+=coef; - } - X+=u; Y+=v; - } - } break; - case 1 : { // Linear interpolation - for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { - const float - u = (float)(W._linear_atXY(X,Y,0,0)), - v = (float)(W._linear_atXY(X,Y,0,1)); - if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } - else { - const float coef = (float)std::exp(-l*l/fsigma2); - cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); - S+=coef; - } - X+=u; Y+=v; - } - } break; - default : { // 2nd-order Runge-kutta interpolation - for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { - const float - u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)), - v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)), - u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)), - v = (float)(W._linear_atXY(X + u0,Y + v0,0,1)); - if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } - else { - const float coef = (float)std::exp(-l*l/fsigma2); - cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); - S+=coef; - } - X+=u; Y+=v; - } - } - } - Tfloat *ptrd = res.data(x,y); - if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } - else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; } - } - } _cimg_abort_catch_omp2 - } - } - const Tfloat *ptrs = res._data; - cimg_for(*this,ptrd,T) { - const Tfloat val = *(ptrs++)/N; - *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val); - } - } - cimg_abort_test; - return *this; - } - - //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance. - template<typename t> - CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G, - const float amplitude=60, const float dl=0.8f, const float da=30, - const float gauss_prec=2, const unsigned int interpolation_type=0, - const bool is_fast_approx=true) const { - return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); - } - - //! Blur image anisotropically, in an edge-preserving way. - /** - \param amplitude Amplitude of the smoothing. - \param sharpness Sharpness. - \param anisotropy Anisotropy. - \param alpha Standard deviation of the gradient blur. - \param sigma Standard deviation of the structure tensor blur. - \param dl Spatial discretization. - \param da Angular discretization. - \param gauss_prec Precision of the diffusion process. - \param interpolation_type Interpolation scheme. - Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>. - \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. - **/ - CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, - const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, - const float gauss_prec=2, const unsigned int interpolation_type=0, - const bool is_fast_approx=true) { - const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100; - const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; - return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3), - amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); - } - - //! Blur image anisotropically, in an edge-preserving way \newinstance. - CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, - const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, - const float da=30, const float gauss_prec=2, - const unsigned int interpolation_type=0, - const bool is_fast_approx=true) const { - return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec, - interpolation_type,is_fast_approx); - } - - //! Blur image, with the joint bilateral filter. - /** - \param guide Image used to model the smoothing weights. - \param sigma_x Amount of blur along the X-axis. - \param sigma_y Amount of blur along the Y-axis. - \param sigma_z Amount of blur along the Z-axis. - \param sigma_r Amount of blur along the value axis. - \param sampling_x Amount of downsampling along the X-axis used for the approximation. - Defaults (0) to sigma_x. - \param sampling_y Amount of downsampling along the Y-axis used for the approximation. - Defaults (0) to sigma_y. - \param sampling_z Amount of downsampling along the Z-axis used for the approximation. - Defaults (0) to sigma_z. - \param sampling_r Amount of downsampling along the value axis used for the approximation. - Defaults (0) to sigma_r. - \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 - (extended for 3D volumetric images). - It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m - **/ - template<typename t> - CImg<T>& blur_bilateral(const CImg<t>& guide, - const float sigma_x, const float sigma_y, - const float sigma_z, const float sigma_r, - const float sampling_x, const float sampling_y, - const float sampling_z, const float sampling_r) { - if (!is_sameXYZ(guide)) - throw CImgArgumentException(_cimg_instance - "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", - cimg_instance, - guide._width,guide._height,guide._depth,guide._spectrum,guide._data); - if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this; - T edge_min, edge_max = guide.max_min(edge_min); - if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z); - const float - edge_delta = (float)(edge_max - edge_min), - _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100, - _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100, - _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100, - _sigma_r = sigma_r>=0?sigma_r:-sigma_r*(edge_max - edge_min)/100, - _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f), - _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f), - _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f), - _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256), - derived_sigma_x = _sigma_x / _sampling_x, - derived_sigma_y = _sigma_y / _sampling_y, - derived_sigma_z = _sigma_z / _sampling_z, - derived_sigma_r = _sigma_r / _sampling_r; - const int - padding_x = (int)(2*derived_sigma_x) + 1, - padding_y = (int)(2*derived_sigma_y) + 1, - padding_z = (int)(2*derived_sigma_z) + 1, - padding_r = (int)(2*derived_sigma_r) + 1; - const unsigned int - bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x), - by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y), - bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z), - br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r); - if (bx>0 || by>0 || bz>0 || br>0) { - const bool is_3d = (_depth>1); - if (is_3d) { // 3D version of the algorithm - CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); - cimg_forC(*this,c) { - const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum); - bgrid.fill(0); bgridw.fill(0); - cimg_forXYZ(*this,x,y,z) { - const T val = (*this)(x,y,z,c); - const float edge = (float)_guide(x,y,z); - const int - X = (int)cimg::round(x/_sampling_x) + padding_x, - Y = (int)cimg::round(y/_sampling_y) + padding_y, - Z = (int)cimg::round(z/_sampling_z) + padding_z, - R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; - bgrid(X,Y,Z,R)+=(float)val; - bgridw(X,Y,Z,R)+=1; - } - bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); - bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); - - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if_size(size(),4096)) - cimg_forXYZ(*this,x,y,z) { - const float edge = (float)_guide(x,y,z); - const float - X = x/_sampling_x + padding_x, - Y = y/_sampling_y + padding_y, - Z = z/_sampling_z + padding_z, - R = (edge - edge_min)/_sampling_r + padding_r; - const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R); - (*this)(x,y,z,c) = (T)(bval0/bval1); - } - } - } else { // 2D version of the algorithm - CImg<floatT> bgrid(bx,by,br,2); - cimg_forC(*this,c) { - const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum); - bgrid.fill(0); - cimg_forXY(*this,x,y) { - const T val = (*this)(x,y,c); - const float edge = (float)_guide(x,y); - const int - X = (int)cimg::round(x/_sampling_x) + padding_x, - Y = (int)cimg::round(y/_sampling_y) + padding_y, - R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; - bgrid(X,Y,R,0)+=(float)val; - bgrid(X,Y,R,1)+=1; - } - bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false); - - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if_size(size(),4096)) - cimg_forXY(*this,x,y) { - const float edge = (float)_guide(x,y); - const float - X = x/_sampling_x + padding_x, - Y = y/_sampling_y + padding_y, - R = (edge - edge_min)/_sampling_r + padding_r; - const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1); - (*this)(x,y,c) = (T)(bval0/bval1); - } - } - } - } - return *this; - } - - //! Blur image, with the joint bilateral filter \newinstance. - template<typename t> - CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide, - const float sigma_x, const float sigma_y, - const float sigma_z, const float sigma_r, - const float sampling_x, const float sampling_y, - const float sampling_z, const float sampling_r) const { - return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r, - sampling_x,sampling_y,sampling_z,sampling_r); - } - - //! Blur image using the joint bilateral filter. - /** - \param guide Image used to model the smoothing weights. - \param sigma_s Amount of blur along the XYZ-axes. - \param sigma_r Amount of blur along the value axis. - \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s. - \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r. - **/ - template<typename t> - CImg<T>& blur_bilateral(const CImg<t>& guide, - const float sigma_s, const float sigma_r, - const float sampling_s=0, const float sampling_r=0) { - const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; - return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r); - } - - //! Blur image using the bilateral filter \newinstance. - template<typename t> - CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide, - const float sigma_s, const float sigma_r, - const float sampling_s=0, const float sampling_r=0) const { - return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r); - } - - // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()). - /* - \param ptr the pointer of the data - \param N size of the data - \param boxsize Size of the box filter (can be subpixel). - \param off the offset between two data point - \param order the order of the filter 0 (smoothing), 1st derivtive and 2nd derivative. - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>. - */ - static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off, - const int order, const bool boundary_conditions, - const unsigned int nb_iter) { - // Smooth. - if (boxsize>1 && nb_iter) { - const int w2 = (int)(boxsize - 1)/2; - const unsigned int winsize = 2*w2 + 1U; - const double frac = (boxsize - winsize)/2.; - CImg<T> win(winsize); - for (unsigned int iter = 0; iter<nb_iter; ++iter) { - Tdouble sum = 0; // window sum - for (int x = -w2; x<=w2; ++x) { - win[x + w2] = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x); - sum+=win[x + w2]; - } - int ifirst = 0, ilast = 2*w2; - T - prev = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-w2 - 1), - next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,w2 + 1); - for (int x = 0; x < N - 1; ++x) { - const double sum2 = sum + frac * (prev + next); - ptr[x*off] = (T)(sum2/boxsize); - prev = win[ifirst]; - sum-=prev; - ifirst = (int)((ifirst + 1)%winsize); - ilast = (int)((ilast + 1)%winsize); - win[ilast] = next; - sum+=next; - next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + w2 + 2); - } - const double sum2 = sum + frac * (prev + next); - ptr[(N - 1)*off] = (T)(sum2/boxsize); - } - } - - // Derive. - switch (order) { - case 0 : - break; - case 1 : { - Tfloat - p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1), - c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0), - n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1); - for (int x = 0; x<N - 1; ++x) { - ptr[x*off] = (T)((n-p)/2.); - p = c; - c = n; - n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2); - } - ptr[(N - 1)*off] = (T)((n-p)/2.); - } break; - case 2: { - Tfloat - p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1), - c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0), - n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1); - for (int x = 0; x<N - 1; ++x) { - ptr[x*off] = (T)(n - 2*c + p); - p = c; - c = n; - n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2); - } - ptr[(N - 1)*off] = (T)(n - 2*c + p); - } break; - } - } - - static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off, - const bool boundary_conditions, const int x) { - if (x<0) return boundary_conditions?ptr[0]:T(); - if (x>=N) return boundary_conditions?ptr[(N - 1)*off]:T(); - return ptr[x*off]; - } - - // Apply box filter of order 0,1,2. - /** - \param boxsize Size of the box window (can be subpixel) - \param order the order of the filter 0,1 or 2. - \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>. - \param nb_iter Number of filter iterations. - **/ - CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x', - const bool boundary_conditions=true, - const unsigned int nb_iter=1) { - if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this; - const char naxis = cimg::lowercase(axis); - const float nboxsize = boxsize>=0?boxsize:-boxsize* - (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; - switch (naxis) { - case 'x' : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forYZC(*this,y,z,c) - _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter); - } break; - case 'y' : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXZC(*this,x,z,c) - _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter); - } break; - case 'z' : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXYC(*this,x,y,c) - _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter); - } break; - default : { - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth*_spectrum>=16)) - cimg_forXYZ(*this,x,y,z) - _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth, - order,boundary_conditions,nb_iter); - } - } - return *this; - } - - // Apply box filter of order 0,1 or 2 \newinstance. - CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x', - const bool boundary_conditions=true, - const unsigned int nb_iter=1) const { - return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter); - } - - //! Blur image with a box filter. - /** - \param boxsize_x Size of the box window, along the X-axis (can be subpixel). - \param boxsize_y Size of the box window, along the Y-axis (can be subpixel). - \param boxsize_z Size of the box window, along the Z-axis (can be subpixel). - \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>. - \param nb_iter Number of filter iterations. - \note - - This is a recursive algorithm, not depending on the values of the box kernel size. - \see blur(). - **/ - CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, - const bool boundary_conditions=true, - const unsigned int nb_iter=1) { - if (is_empty()) return *this; - if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter); - if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter); - if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter); - return *this; - } - - //! Blur image with a box filter \newinstance. - CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, - const bool boundary_conditions=true) const { - return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions); - } - - //! Blur image with a box filter. - /** - \param boxsize Size of the box window (can be subpixel). - \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a - \see deriche(), vanvliet(). - **/ - CImg<T>& blur_box(const float boxsize, const bool boundary_conditions=true) { - const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100; - return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions); - } - - //! Blur image with a box filter \newinstance. - CImg<Tfloat> get_blur_box(const float boxsize, const bool boundary_conditions=true) const { - return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions); - } - - //! Blur image, with the image guided filter. - /** - \param guide Image used to guide the smoothing process. - \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size. - \param regularization Regularization parameter. - If negative, it is expressed as a percentage of the guide value range. - \note This method implements the filtering algorithm described in: - He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence, - IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013 - **/ - template<typename t> - CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) { - return get_blur_guided(guide,radius,regularization).move_to(*this); - } - - //! Blur image, with the image guided filter \newinstance. - template<typename t> - CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const { - if (!is_sameXYZ(guide)) - throw CImgArgumentException(_cimg_instance - "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", - cimg_instance, - guide._width,guide._height,guide._depth,guide._spectrum,guide._data); - if (is_empty() || !radius) return *this; - const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100); - float _regularization = regularization; - if (regularization<0) { - T edge_min, edge_max = guide.max_min(edge_min); - if (edge_min==edge_max) return *this; - _regularization = -regularization*(edge_max - edge_min)/100; - } - _regularization = std::max(_regularization,0.01f); - const unsigned int psize = (unsigned int)(1 + 2*_radius); - CImg<Tfloat> - mean_p = get_blur_box(psize,true), - mean_I = guide.get_blur_box(psize,true).resize(mean_p), - cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I), - var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(), - &a = cov_Ip.div(var_I+=_regularization), - &b = mean_p-=a.get_mul(mean_I); - a.blur_box(psize,true); - b.blur_box(psize,true); - return a.mul(guide)+=b; - } - - //! Blur image using patch-based space. - /** - \param sigma_s Amount of blur along the XYZ-axes. - \param sigma_p Amount of blur along the value axis. - \param patch_size Size of the patches. - \param lookup_size Size of the window to search similar patches. - \param smoothness Smoothness for the patch comparison. - \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. - **/ - CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3, - const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { - if (is_empty() || !patch_size || !lookup_size) return *this; - return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this); - } - - //! Blur image using patch-based space \newinstance. - CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3, - const unsigned int lookup_size=4, const float smoothness=0, - const bool is_fast_approx=true) const { - -#define _cimg_blur_patch3d_fast(N) \ - cimg_for##N##XYZ(res,x,y,z) { \ - T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \ - const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ - x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ - float sum_weights = 0; \ - cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \ - if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))<sigma_p3) { \ - T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \ - float distance2 = 0; \ - pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \ - distance2/=Pnorm; \ - const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \ - alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.f:1.f; \ - sum_weights+=weight; \ - cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \ - } \ - if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \ - else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ - } - -#define _cimg_blur_patch3d(N) \ - cimg_for##N##XYZ(res,x,y,z) { \ - T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \ - const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ - x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ - float sum_weights = 0, weight_max = 0; \ - cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \ - T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \ - float distance2 = 0; \ - pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \ - distance2/=Pnorm; \ - const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \ - alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \ - if (weight>weight_max) weight_max = weight; \ - sum_weights+=weight; \ - cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \ - } \ - sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \ - if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \ - else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ - } - -#define _cimg_blur_patch2d_fast(N) \ - cimg_for##N##XY(res,x,y) { \ - T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \ - const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ - float sum_weights = 0; \ - cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \ - if (cimg::abs((Tfloat)img(x,y,0,0) - (Tfloat)img(p,q,0,0))<sigma_p3) { \ - T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \ - float distance2 = 0; \ - pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \ - distance2/=Pnorm; \ - const float dx = (float)p - x, dy = (float)q - y, \ - alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.f:1.f; \ - sum_weights+=weight; \ - cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \ - } \ - if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \ - else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ - } - -#define _cimg_blur_patch2d(N) \ - cimg_for##N##XY(res,x,y) { \ - T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \ - const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ - float sum_weights = 0, weight_max = 0; \ - cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \ - T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \ - float distance2 = 0; \ - pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \ - distance2/=Pnorm; \ - const float dx = (float)p - x, dy = (float)q - y, \ - alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \ - if (weight>weight_max) weight_max = weight; \ - sum_weights+=weight; \ - cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \ - } \ - sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \ - if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \ - else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ - } - - if (is_empty() || !patch_size || !lookup_size) return +*this; - CImg<Tfloat> res(_width,_height,_depth,_spectrum,0); - const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this; - CImg<T> P(patch_size*patch_size*_spectrum), Q(P); - const float - nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100, - sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p, - Pnorm = P.size()*sigma_p2; - const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1; - const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size; - cimg::unused(N2,N3); - if (_depth>1) switch (patch_size) { // 3D - case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break; - case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break; - default : { - const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; - if (is_fast_approx) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && - res._height*res._depth>=4) - private(P,Q)) - cimg_forXYZ(res,x,y,z) { // Fast - P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); - const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, - x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; - float sum_weights = 0; - cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) - if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))<sigma_p3) { - (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P; - const float - dx = (float)x - p, dy = (float)y - q, dz = (float)z - r, - distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2), - weight = distance2>3?0.f:1.f; - sum_weights+=weight; - cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); - } - if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; - else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); - } else - cimg_pragma_openmp(parallel for collapse(2) - if (res._width>=32 && res._height*res._depth>=4) firstprivate(P,Q)) - cimg_forXYZ(res,x,y,z) { // Exact - P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); - const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, - x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; - float sum_weights = 0, weight_max = 0; - cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { - (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P; - const float - dx = (float)x - p, dy = (float)y - q, dz = (float)z - r, - distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2), - weight = (float)std::exp(-distance2); - if (weight>weight_max) weight_max = weight; - sum_weights+=weight; - cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); - } - sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); - if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; - else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); - } - } - } else switch (patch_size) { // 2D - case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break; - case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break; - case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break; - case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break; - case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break; - case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break; - case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break; - case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break; - default : { // Fast - const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; - if (is_fast_approx) - cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) - firstprivate(P,Q)) - cimg_forXY(res,x,y) { // Fast - P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); - const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; - float sum_weights = 0; - cimg_for_inXY(res,x0,y0,x1,y1,p,q) - if ((Tfloat)cimg::abs(img(x,y,0) - (Tfloat)img(p,q,0))<sigma_p3) { - (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P; - const float - dx = (float)x - p, dy = (float)y - q, - distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2), - weight = distance2>3?0.f:1.f; - sum_weights+=weight; - cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); - } - if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; - else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); - } else - cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) - firstprivate(P,Q)) - cimg_forXY(res,x,y) { // Exact - P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); - const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; - float sum_weights = 0, weight_max = 0; - cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { - (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P; - const float - dx = (float)x - p, dy = (float)y - q, - distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2), - weight = (float)std::exp(-distance2); - if (weight>weight_max) weight_max = weight; - sum_weights+=weight; - cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); - } - sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); - if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; - else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c)); - } - } - } - return res; - } - - //! Blur image with the median filter. - /** - \param n Size of the median filter. - \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation. - **/ - CImg<T>& blur_median(const unsigned int n, const float threshold=0) { - if (!n) return *this; - return get_blur_median(n,threshold).move_to(*this); - } - - //! Blur image with the median filter \newinstance. - CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const { - if (is_empty() || n<=1) return +*this; - CImg<T> res(_width,_height,_depth,_spectrum); - T *ptrd = res._data; - cimg::unused(ptrd); - const int hr = (int)n/2, hl = n - hr - 1; - if (res._depth!=1) { // 3D - if (threshold>0) - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && - _height*_depth*_spectrum>=4)) - cimg_forXYZC(*this,x,y,z,c) { // With threshold - const int - x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, - nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, - nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; - const Tfloat val0 = (Tfloat)(*this)(x,y,z,c); - CImg<T> values(n*n*n); - unsigned int nb_values = 0; - T *ptrd = values.data(); - cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r) - if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; } - res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c); - } - else - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && - _height*_depth*_spectrum>=4)) - cimg_forXYZC(*this,x,y,z,c) { // Without threshold - const int - x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, - nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, - nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; - res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median(); - } - } else { - if (threshold>0) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && - _height*_spectrum>=4)) - cimg_forXYC(*this,x,y,c) { // With threshold - const int - x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, - nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, - nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; - const Tfloat val0 = (Tfloat)(*this)(x,y,c); - CImg<T> values(n*n); - unsigned int nb_values = 0; - T *ptrd = values.data(); - cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q) - if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; } - res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c); - } - else { - const int - w1 = width() - 1, h1 = height() - 1, - w2 = width() - 2, h2 = height() - 2, - w3 = width() - 3, h3 = height() - 3, - w4 = width() - 4, h4 = height() - 4; - switch (n) { // Without threshold - case 3 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - CImg<T> I(9); - cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T) - res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]); - cimg_for_borderXY(*this,x,y,1) - res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c, - std::min(w1,x + 1),std::min(h1,y + 1),0,c).median(); - } - } break; - case 5 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - CImg<T> I(25); - cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T) - res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4], - I[5],I[6],I[7],I[8],I[9], - I[10],I[11],I[12],I[13],I[14], - I[15],I[16],I[17],I[18],I[19], - I[20],I[21],I[22],I[23],I[24]); - cimg_for_borderXY(*this,x,y,2) - res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c, - std::min(w1,x + 2),std::min(h1,y + 2),0,c).median(); - } - } break; - case 7 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - CImg<T> I(49); - cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T) - res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6], - I[7],I[8],I[9],I[10],I[11],I[12],I[13], - I[14],I[15],I[16],I[17],I[18],I[19],I[20], - I[21],I[22],I[23],I[24],I[25],I[26],I[27], - I[28],I[29],I[30],I[31],I[32],I[33],I[34], - I[35],I[36],I[37],I[38],I[39],I[40],I[41], - I[42],I[43],I[44],I[45],I[46],I[47],I[48]); - cimg_for_borderXY(*this,x,y,3) - res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c, - std::min(w1,x + 3),std::min(h1,y + 3),0,c).median(); - } - } break; - default : { - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && - _height*_spectrum>=4)) - cimg_forXYC(*this,x,y,c) { - const int - x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, - nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, - nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; - res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); - } - } - } - } - } - return res; - } - - //! Sharpen image. - /** - \param amplitude Sharpening amplitude - \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>. - \param edge Edge threshold (shock filters only). - \param alpha Gradient smoothness (shock filters only). - \param sigma Tensor smoothness (shock filters only). - **/ - CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, - const float alpha=0, const float sigma=0) { - if (is_empty()) return *this; - T val_min, val_max = max_min(val_min); - const float nedge = edge/2; - CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum); - - if (_depth>1) { // 3D - if (sharpen_type) { // Shock filters - CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); - if (sigma>0) G.blur(sigma); - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && - _height*_depth>=16)) - cimg_forYZ(G,y,z) { - Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1), - *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3); - CImg<Tfloat> val, vec; - cimg_forX(G,x) { - G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); - if (val[0]<0) val[0] = 0; - if (val[1]<0) val[1] = 0; - if (val[2]<0) val[2] = 0; - *(ptrG0++) = vec(0,0); - *(ptrG1++) = vec(0,1); - *(ptrG2++) = vec(0,2); - *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge); - } - } - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - const Tfloat - u = G(x,y,z,0), - v = G(x,y,z,1), - w = G(x,y,z,2), - amp = G(x,y,z,3), - ixx = Incc + Ipcc - 2*Iccc, - ixy = (Innc + Ippc - Inpc - Ipnc)/4, - ixz = (Incn + Ipcp - Incp - Ipcn)/4, - iyy = Icnc + Icpc - 2*Iccc, - iyz = (Icnn + Icpp - Icnp - Icpn)/4, - izz = Iccn + Iccp - 2*Iccc, - ixf = Incc - Iccc, - ixb = Iccc - Ipcc, - iyf = Icnc - Iccc, - iyb = Iccc - Icpc, - izf = Iccn - Iccc, - izb = Iccc - Iccp, - itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, - it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb), - veloc = -amp*cimg::sign(itt)*cimg::abs(it); - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } - _veloc_max[c] = veloc_max; - } - } else // Inverse diffusion - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc; - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } - _veloc_max[c] = veloc_max; - } - } else { // 2D - if (sharpen_type) { // Shock filters - CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); - if (sigma>0) G.blur(sigma); - cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && - _height>=(cimg_openmp_sizefactor)*16)) - cimg_forY(G,y) { - CImg<Tfloat> val, vec; - Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2); - cimg_forX(G,x) { - G.get_tensor_at(x,y).symmetric_eigen(val,vec); - if (val[0]<0) val[0] = 0; - if (val[1]<0) val[1] = 0; - *(ptrG0++) = vec(0,0); - *(ptrG1++) = vec(0,1); - *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge); - } - } - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) { - const Tfloat - u = G(x,y,0), - v = G(x,y,1), - amp = G(x,y,2), - ixx = Inc + Ipc - 2*Icc, - ixy = (Inn + Ipp - Inp - Ipn)/4, - iyy = Icn + Icp - 2*Icc, - ixf = Inc - Icc, - ixb = Icc - Ipc, - iyf = Icn - Icc, - iyb = Icc - Icp, - itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, - it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb), - veloc = -amp*cimg::sign(itt)*cimg::abs(it); - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } - _veloc_max[c] = veloc_max; - } - } else // Inverse diffusion - cimg_forC(*this,c) { - Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) { - const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc; - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } - _veloc_max[c] = veloc_max; - } - } - const Tfloat veloc_max = _veloc_max.max(); - if (veloc_max<=0) return *this; - return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this); - } - - //! Sharpen image \newinstance. - CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, - const float alpha=0, const float sigma=0) const { - return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma); - } - - //! Return image gradient. - /** - \param axes Axes considered for the gradient computation, as a C-string (e.g "xy"). - \param scheme = Numerical scheme used for the gradient computation: - - -1 = Backward finite differences - - 0 = Centered finite differences - - 1 = Forward finite differences - - 2 = Using Sobel kernels - - 3 = Using rotation invariant kernels - - 4 = Using Deriche recusrsive filter. - - 5 = Using Van Vliet recusrsive filter. - **/ - CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const { - CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum); - bool is_3d = false; - if (axes) { - for (unsigned int a = 0; axes[a]; ++a) { - const char axis = cimg::lowercase(axes[a]); - switch (axis) { - case 'x' : case 'y' : break; - case 'z' : is_3d = true; break; - default : - throw CImgArgumentException(_cimg_instance - "get_gradient(): Invalid specified axis '%c'.", - cimg_instance, - axis); - } - } - } else is_3d = (_depth>1); - if (is_3d) { - CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad); - switch (scheme) { // 3D - case -1 : { // Backward finite differences - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - const ulongT off = (ulongT)c*_width*_height*_depth; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Iccc - Ipcc; - *(ptrd1++) = Iccc - Icpc; - *(ptrd2++) = Iccc - Iccp; - } - } - } break; - case 1 : { // Forward finite differences - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - const ulongT off = (ulongT)c*_width*_height*_depth; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off; - CImg_2x2x2(I,Tfloat); - cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Incc - Iccc; - *(ptrd1++) = Icnc - Iccc; - *(ptrd2++) = Iccn - Iccc; - } - } - } break; - case 4 : { // Deriche filter with low standard variation - grad[0] = get_deriche(0,1,'x'); - grad[1] = get_deriche(0,1,'y'); - grad[2] = get_deriche(0,1,'z'); - } break; - case 5 : { // Van Vliet filter with low standard variation - grad[0] = get_vanvliet(0,1,'x'); - grad[1] = get_vanvliet(0,1,'y'); - grad[2] = get_vanvliet(0,1,'z'); - } break; - default : { // Central finite differences - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - const ulongT off = (ulongT)c*_width*_height*_depth; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = (Incc - Ipcc)/2; - *(ptrd1++) = (Icnc - Icpc)/2; - *(ptrd2++) = (Iccn - Iccp)/2; - } - } - } - } - } else switch (scheme) { // 2D - case -1 : { // Backward finite differences - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Icc - Ipc; - *(ptrd1++) = Icc - Icp; - } - } - } break; - case 1 : { // Forward finite differences - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_2x2(I,Tfloat); - cimg_for2x2(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Inc - Icc; - *(ptrd1++) = Icn - Icc; - } - } - } break; - case 2 : { // Sobel scheme - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = -Ipp - 2*Ipc - Ipn + Inp + 2*Inc + Inn; - *(ptrd1++) = -Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn; - } - } - } break; - case 3 : { // Rotation invariant kernel - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_3x3(I,Tfloat); - const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1)); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; - *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; - } - } - } break; - case 4 : { // Van Vliet filter with low standard variation - grad[0] = get_deriche(0,1,'x'); - grad[1] = get_deriche(0,1,'y'); - } break; - case 5 : { // Deriche filter with low standard variation - grad[0] = get_vanvliet(0,1,'x'); - grad[1] = get_vanvliet(0,1,'y'); - } break; - default : { // Central finite differences - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = (Inc - Ipc)/2; - *(ptrd1++) = (Icn - Icp)/2; - } - } - } - } - if (!axes) return grad; - CImgList<Tfloat> res; - for (unsigned int l = 0; axes[l]; ++l) { - const char axis = cimg::lowercase(axes[l]); - switch (axis) { - case 'x' : res.insert(grad[0]); break; - case 'y' : res.insert(grad[1]); break; - case 'z' : res.insert(grad[2]); break; - } - } - grad.assign(); - return res; - } - - //! Return image hessian. - /** - \param axes Axes considered for the hessian computation, as a C-string (e.g "xy"). - **/ - CImgList<Tfloat> get_hessian(const char *const axes=0) const { - CImgList<Tfloat> res; - const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz"; - if (!axes) naxes = _depth>1?def_axes3d:def_axes2d; - const unsigned int lmax = (unsigned int)std::strlen(naxes); - if (lmax%2) - throw CImgArgumentException(_cimg_instance - "get_hessian(): Invalid specified axes '%s'.", - cimg_instance, - naxes); - - res.assign(lmax/2,_width,_height,_depth,_spectrum); - if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3D - - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - const ulongT off = (ulongT)c*_width*_height*_depth; - Tfloat - *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off, - *ptrd3 = res[3]._data + off, *ptrd4 = res[4]._data + off, *ptrd5 = res[5]._data + off; - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Ipcc + Incc - 2*Iccc; // Ixx - *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy - *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz - *(ptrd3++) = Icpc + Icnc - 2*Iccc; // Iyy - *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz - *(ptrd5++) = Iccn + Iccp - 2*Iccc; // Izz - } - } - } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2D - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height; - Tfloat *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off; - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Ipc + Inc - 2*Icc; // Ixx - *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy - *(ptrd2++) = Icp + Icn - 2*Icc; // Iyy - } - } - } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes - const unsigned int l2 = l/2; - char axis1 = naxes[l++], axis2 = naxes[l++]; - if (axis1>axis2) cimg::swap(axis1,axis2); - bool valid_axis = false; - if (axis1=='x' && axis2=='x') { // Ixx - valid_axis = true; - cimg_pragma_openmp(parallel for collapse(2) - cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - Tfloat *ptrd = res[l2].data(0,0,z,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc; - } - } - else if (axis1=='x' && axis2=='y') { // Ixy - valid_axis = true; - cimg_pragma_openmp(parallel for collapse(2) - cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - Tfloat *ptrd = res[l2].data(0,0,z,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4; - } - } - else if (axis1=='x' && axis2=='z') { // Ixz - valid_axis = true; - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = res[l2].data(0,0,0,c); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4; - } - } - else if (axis1=='y' && axis2=='y') { // Iyy - valid_axis = true; - cimg_pragma_openmp(parallel for collapse(2) - cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forZC(*this,z,c) { - Tfloat *ptrd = res[l2].data(0,0,z,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc; - } - } - else if (axis1=='y' && axis2=='z') { // Iyz - valid_axis = true; - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = res[l2].data(0,0,0,c); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4; - } - } - else if (axis1=='z' && axis2=='z') { // Izz - valid_axis = true; - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = res[l2].data(0,0,0,c); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc; - } - } - else if (!valid_axis) - throw CImgArgumentException(_cimg_instance - "get_hessian(): Invalid specified axes '%s'.", - cimg_instance, - naxes); - } - return res; - } - - //! Compute image Laplacian. - CImg<T>& laplacian() { - return get_laplacian().move_to(*this); - } - - //! Compute image Laplacian \newinstance. - CImg<Tfloat> get_laplacian() const { - if (is_empty()) return CImg<Tfloat>(); - CImg<Tfloat> res(_width,_height,_depth,_spectrum); - if (_depth>1) { // 3D - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = res.data(0,0,0,c); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc; - } - } else if (_height>1) { // 2D - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = res.data(0,0,0,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc; - } - } else { // 1D - cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 && - _height*_depth*_spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd = res.data(0,0,0,c); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc; - } - } - return res; - } - - //! Compute the structure tensor field of an image. - /** - \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt> - **/ - CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) { - return get_structure_tensors(is_fwbw_scheme).move_to(*this); - } - - //! Compute the structure tensor field of an image \newinstance. - CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const { - if (is_empty()) return *this; - CImg<Tfloat> res; - if (_depth>1) { // 3D - res.assign(_width,_height,_depth,6,0); - if (!is_fwbw_scheme) { // Classical central finite differences - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat - *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), - *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - const Tfloat - ix = (Incc - Ipcc)/2, - iy = (Icnc - Icpc)/2, - iz = (Iccn - Iccp)/2; - *(ptrd0++)+=ix*ix; - *(ptrd1++)+=ix*iy; - *(ptrd2++)+=ix*iz; - *(ptrd3++)+=iy*iy; - *(ptrd4++)+=iy*iz; - *(ptrd5++)+=iz*iz; - } - } - } else { // Forward/backward finite differences - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && - _spectrum>=2)) - cimg_forC(*this,c) { - Tfloat - *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), - *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - const Tfloat - ixf = Incc - Iccc, ixb = Iccc - Ipcc, - iyf = Icnc - Iccc, iyb = Iccc - Icpc, - izf = Iccn - Iccc, izb = Iccc - Iccp; - *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; - *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2; - *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; - *(ptrd5++)+=(izf*izf + izb*izb)/2; - } - } - } - } else { // 2D - res.assign(_width,_height,_depth,3,0); - if (!is_fwbw_scheme) { // Classical central finite differences - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) { - const Tfloat - ix = (Inc - Ipc)/2, - iy = (Icn - Icp)/2; - *(ptrd0++)+=ix*ix; - *(ptrd1++)+=ix*iy; - *(ptrd2++)+=iy*iy; - } - } - } else { // Forward/backward finite differences (version 2) - cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && - _depth*_spectrum>=2)) - cimg_forC(*this,c) { - Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); - CImg_3x3(I,Tfloat); - cimg_for3x3(*this,x,y,0,c,I,Tfloat) { - const Tfloat - ixf = Inc - Icc, ixb = Icc - Ipc, - iyf = Icn - Icc, iyb = Icc - Icp; - *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; - } - } - } - } - return res; - } - - //! Compute field of diffusion tensors for edge-preserving smoothing. - /** - \param sharpness Sharpness - \param anisotropy Anisotropy - \param alpha Standard deviation of the gradient blur. - \param sigma Standard deviation of the structure tensor blur. - \param is_sqrt Tells if the square root of the tensor field is computed instead. - **/ - CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, - const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) { - CImg<Tfloat> res; - const float - nsharpness = std::max(sharpness,1e-5f), - power1 = (is_sqrt?0.5f:1)*nsharpness, - power2 = power1/(1e-7f + 1 - anisotropy); - blur(alpha).normalize(0,(T)255); - - if (_depth>1) { // 3D - get_structure_tensors().move_to(res).blur(sigma); - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height*_depth>=(cimg_openmp_sizefactor)*256)) - cimg_forYZ(*this,y,z) { - Tfloat - *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2), - *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5); - CImg<floatT> val(3), vec(3,3); - cimg_forX(*this,x) { - res.get_tensor_at(x,y,z).symmetric_eigen(val,vec); - const float - _l1 = val[2], _l2 = val[1], _l3 = val[0], - l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0, - ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), - vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), - wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), - n1 = (float)std::pow(1 + l1 + l2 + l3,-power1), - n2 = (float)std::pow(1 + l1 + l2 + l3,-power2); - *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx; - *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy; - *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz; - *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy; - *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz; - *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz; - } - } - } else { // for 2D images - get_structure_tensors().move_to(res).blur(sigma); - cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && - _height>=(cimg_openmp_sizefactor)*256)) - cimg_forY(*this,y) { - Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2); - CImg<floatT> val(2), vec(2,2); - cimg_forX(*this,x) { - res.get_tensor_at(x,y).symmetric_eigen(val,vec); - const float - _l1 = val[1], _l2 = val[0], - l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, - ux = vec(1,0), uy = vec(1,1), - vx = vec(0,0), vy = vec(0,1), - n1 = (float)std::pow(1 + l1 + l2,-power1), - n2 = (float)std::pow(1 + l1 + l2,-power2); - *(ptrd0++) = n1*ux*ux + n2*vx*vx; - *(ptrd1++) = n1*ux*uy + n2*vx*vy; - *(ptrd2++) = n1*uy*uy + n2*vy*vy; - } - } - } - return res.move_to(*this); - } - - //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance. - CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, - const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const { - return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt); - } - - //! Estimate displacement field between two images. - /** - \param source Reference image. - \param smoothness Smoothness of estimated displacement field. - \param precision Precision required for algorithm convergence. - \param nb_scales Number of scales used to estimate the displacement field. - \param iteration_max Maximum number of iterations allowed for one scale. - \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)). - \param guide Image used as the initial correspondence estimate for the algorithm. - 'guide' may have a last channel with boolean values (0=false | other=true) that - tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). - **/ - CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.f, - const unsigned int nb_scales=0, const unsigned int iteration_max=10000, - const bool is_backward=false, - const CImg<floatT>& guide=CImg<floatT>::const_empty()) { - return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). - move_to(*this); - } - - //! Estimate displacement field between two images \newinstance. - CImg<floatT> get_displacement(const CImg<T>& source, - const float smoothness=0.1f, const float precision=5.f, - const unsigned int nb_scales=0, const unsigned int iteration_max=10000, - const bool is_backward=false, - const CImg<floatT>& guide=CImg<floatT>::const_empty()) const { - if (is_empty() || !source) return +*this; - if (!is_sameXYZC(source)) - throw CImgArgumentException(_cimg_instance - "displacement(): Instance and source image (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - source._width,source._height,source._depth,source._spectrum,source._data); - if (precision<0) - throw CImgArgumentException(_cimg_instance - "displacement(): Invalid specified precision %g " - "(should be >=0)", - cimg_instance, - precision); - - const bool is_3d = source._depth>1; - const unsigned int constraint = is_3d?3:2; - - if (guide && - (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint)) - throw CImgArgumentException(_cimg_instance - "displacement(): Specified guide (%u,%u,%u,%u,%p) " - "has invalid dimensions.", - cimg_instance, - guide._width,guide._height,guide._depth,guide._spectrum,guide._data); - - const unsigned int - mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height), - _nb_scales = nb_scales>0?nb_scales: - (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1); - - const float _precision = (float)std::pow(10.,-(double)precision); - float sm, sM = source.max_min(sm), tm, tM = max_min(tm); - const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm); - - CImg<floatT> U, V; - floatT bound = 0; - for (int scale = (int)_nb_scales - 1; scale>=0; --scale) { - const float factor = (float)std::pow(1.5,(double)scale); - const unsigned int - _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1, - _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1, - _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1; - if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // Skip too small scales - const CImg<Tfloat> - I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta, - I2 = (get_resize(I1,2)-=tm)/=tdelta; - if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V); - if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3); - else { - if (guide) - guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U); - else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); - } - - float dt = 2, energy = cimg::type<float>::max(); - const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient(); - cimg_abort_init; - - for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) { - cimg_abort_test; - float _energy = 0; - - if (is_3d) { // 3D version - if (smoothness>=0) // Isotropic regularization - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && - _width>=(cimg_openmp_sizefactor)*16) - reduction(+:_energy)) - cimg_forYZ(U,y,z) { - const int - _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y, - _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z; - cimg_for3X(U,x) { - const float - X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0), - Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1), - Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2); - float delta_I = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c)); - else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c)); - cimg_forC(U,c) { - const float - Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)), - Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)), - Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)), - Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c), - Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c), - Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c); - U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) + - smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt); - _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz; - } - if (is_backward) { // Constraint displacement vectors to stay in image - if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x; - if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; - if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; - bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; - bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; - bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; - } else { - if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; - if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; - if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; - bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; - bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; - bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; - } - _energy+=delta_I*delta_I + smoothness*_energy_regul; - } - if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints - U(x,y,z,0) = V(x,y,z,0)/factor; - U(x,y,z,1) = V(x,y,z,1)/factor; - U(x,y,z,2) = V(x,y,z,2)/factor; - } - } else { // Anisotropic regularization - const float nsmoothness = -smoothness; - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && - _width>=(cimg_openmp_sizefactor)*16) - reduction(+:_energy)) - cimg_forYZ(U,y,z) { - const int - _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y, - _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z; - cimg_for3X(U,x) { - const float - X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0), - Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1), - Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2); - float delta_I = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c)); - else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c)); - cimg_forC(U,c) { - const float - Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)), - Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)), - Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)), - N2 = Ux*Ux + Uy*Uy + Uz*Uz, - N = std::sqrt(N2), - N3 = 1e-5f + N2*N, - coef_a = (1 - Ux*Ux/N2)/N, - coef_b = -2*Ux*Uy/N3, - coef_c = -2*Ux*Uz/N3, - coef_d = (1 - Uy*Uy/N2)/N, - coef_e = -2*Uy*Uz/N3, - coef_f = (1 - Uz*Uz/N2)/N, - Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c), - Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c), - Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c), - Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)), - Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)), - Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c)); - U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) + - nsmoothness* ( coef_a*Uxx + coef_b*Uxy + - coef_c*Uxz + coef_d*Uyy + - coef_e*Uyz + coef_f*Uzz )) - )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt); - _energy_regul+=N; - } - if (is_backward) { // Constraint displacement vectors to stay in image - if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x; - if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; - if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; - bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; - bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; - bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; - } else { - if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; - if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; - if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; - bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; - bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; - bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; - } - _energy+=delta_I*delta_I + nsmoothness*_energy_regul; - } - if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints - U(x,y,z,0) = V(x,y,z,0)/factor; - U(x,y,z,1) = V(x,y,z,1)/factor; - U(x,y,z,2) = V(x,y,z,2)/factor; - } - } - } - } else { // 2D version - if (smoothness>=0) // Isotropic regularization - cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && - _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) - cimg_forY(U,y) { - const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y; - cimg_for3X(U,x) { - const float - X = is_backward?x - U(x,y,0):x + U(x,y,0), - Y = is_backward?y - U(x,y,1):y + U(x,y,1); - float delta_I = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c)); - else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c)); - cimg_forC(U,c) { - const float - Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)), - Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)), - Uxx = U(_n1x,y,c) + U(_p1x,y,c), - Uyy = U(x,_n1y,c) + U(x,_p1y,c); - U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) + - smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt); - _energy_regul+=Ux*Ux + Uy*Uy; - } - if (is_backward) { // Constraint displacement vectors to stay in image - if (U(x,y,0)>x) U(x,y,0) = (float)x; - if (U(x,y,1)>y) U(x,y,1) = (float)y; - bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; - bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; - } else { - if (U(x,y,0)<-x) U(x,y,0) = -(float)x; - if (U(x,y,1)<-y) U(x,y,1) = -(float)y; - bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; - bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; - } - _energy+=delta_I*delta_I + smoothness*_energy_regul; - } - if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints - U(x,y,0) = V(x,y,0)/factor; - U(x,y,1) = V(x,y,1)/factor; - } - } else { // Anisotropic regularization - const float nsmoothness = -smoothness; - cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && - _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) - cimg_forY(U,y) { - const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y; - cimg_for3X(U,x) { - const float - X = is_backward?x - U(x,y,0):x + U(x,y,0), - Y = is_backward?y - U(x,y,1):y + U(x,y,1); - float delta_I = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c)); - else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c)); - cimg_forC(U,c) { - const float - Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)), - Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)), - N2 = Ux*Ux + Uy*Uy, - N = std::sqrt(N2), - N3 = 1e-5f + N2*N, - coef_a = Uy*Uy/N3, - coef_b = -2*Ux*Uy/N3, - coef_c = Ux*Ux/N3, - Uxx = U(_n1x,y,c) + U(_p1x,y,c), - Uyy = U(x,_n1y,c) + U(x,_p1y,c), - Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c)); - U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) + - nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/ - (1 + 2*(coef_a + coef_c)*nsmoothness*dt); - _energy_regul+=N; - } - if (is_backward) { // Constraint displacement vectors to stay in image - if (U(x,y,0)>x) U(x,y,0) = (float)x; - if (U(x,y,1)>y) U(x,y,1) = (float)y; - bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; - bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; - } else { - if (U(x,y,0)<-x) U(x,y,0) = -(float)x; - if (U(x,y,1)<-y) U(x,y,1) = -(float)y; - bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; - bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; - } - _energy+=delta_I*delta_I + nsmoothness*_energy_regul; - } - if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints - U(x,y,0) = V(x,y,0)/factor; - U(x,y,1) = V(x,y,1)/factor; - } - } - } - } - const float d_energy = (_energy - energy)/(sw*sh*sd); - if (d_energy<=0 && -d_energy<_precision) break; - if (d_energy>0) dt*=0.5f; - energy = _energy; - } - } - return U; - } - - //! Compute correspondence map between two images, using a patch-matching algorithm. - /** - \param patch_image The image containing the reference patches to match with the instance image. - \param patch_width Width of the patch used for matching. - \param patch_height Height of the patch used for matching. - \param patch_depth Depth of the patch used for matching. - \param nb_iterations Number of patch-match iterations. - \param nb_randoms Number of randomization attempts (per pixel). - \param occ_penalization Penalization factor in score related patch occurences. - \param guide Image used as the initial correspondence estimate for the algorithm. - 'guide' may have a last channel with boolean values (0=false | other=true) that - tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). - \param[out] matching_score Returned as the image of matching scores. - **/ - template<typename t1, typename t2> - CImg<T>& matchpatch(const CImg<T>& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const float occ_penalization, - const CImg<t1> &guide, - CImg<t2> &matching_score) { - return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms,occ_penalization,guide,matching_score).move_to(*this); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \newinstance. - template<typename t1, typename t2> - CImg<intT> get_matchpatch(const CImg<T>& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const float occ_penalization, - const CImg<t1> &guide, - CImg<t2> &matching_score) const { - return _matchpatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms,occ_penalization, - guide,true,matching_score); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \overloading. - template<typename t> - CImg<T>& matchpatch(const CImg<T>& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations=5, - const unsigned int nb_randoms=5, - const float occ_penalization=0, - const CImg<t> &guide=CImg<t>::const_empty()) { - return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms,occ_penalization,guide).move_to(*this); - } - - //! Compute correspondence map between two images, using the patch-match algorithm \overloading. - template<typename t> - CImg<intT> get_matchpatch(const CImg<T>& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations=5, - const unsigned int nb_randoms=5, - const float occ_penalization=0, - const CImg<t> &guide=CImg<t>::const_empty()) const { - return _matchpatch(patch_image,patch_width,patch_height,patch_depth, - nb_iterations,nb_randoms,guide,occ_penalization,false,CImg<T>::empty()); - } - - template<typename t1, typename t2> - CImg<intT> _matchpatch(const CImg<T>& patch_image, - const unsigned int patch_width, - const unsigned int patch_height, - const unsigned int patch_depth, - const unsigned int nb_iterations, - const unsigned int nb_randoms, - const float occ_penalization, - const CImg<t1> &guide, - const bool is_matching_score, - CImg<t2> &matching_score) const { - if (is_empty()) return CImg<intT>::const_empty(); - if (patch_image._spectrum!=_spectrum) - throw CImgArgumentException(_cimg_instance - "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) " - "have different spectrums.", - cimg_instance, - patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, - patch_image._data); - if (patch_width>_width || patch_height>_height || patch_depth>_depth) - throw CImgArgumentException(_cimg_instance - "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " - "of the instance image.", - cimg_instance,patch_width,patch_height,patch_depth); - if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth) - throw CImgArgumentException(_cimg_instance - "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " - "of the patch image image (%u,%u,%u,%u,%p).", - cimg_instance,patch_width,patch_height,patch_depth, - patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, - patch_image._data); - const unsigned int - _constraint = patch_image._depth>1?3:2, - constraint = guide._spectrum>_constraint?_constraint:0; - - if (guide && - (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint)) - throw CImgArgumentException(_cimg_instance - "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions " - "considering instance and patch image (%u,%u,%u,%u,%p).", - cimg_instance, - guide._width,guide._height,guide._depth,guide._spectrum,guide._data, - patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, - patch_image._data); - - CImg<intT> map(_width,_height,_depth,patch_image._depth>1?3:2); - CImg<floatT> score(_width,_height,_depth); - CImg<uintT> occ, loop_order; - ulongT rng = (cimg::_rand(),cimg::rng()); - if (occ_penalization!=0) { - occ.assign(patch_image._width,patch_image._height,patch_image._depth,1,0); - loop_order.assign(_width,_height,_depth,_depth>1?3:2); - cimg_forXYZ(loop_order,x,y,z) { - loop_order(x,y,z,0) = x; - loop_order(x,y,z,1) = y; - if (loop_order._spectrum>2) loop_order(x,y,z,2) = z; - } - cimg_forXYZ(loop_order,x,y,z) { // Randomize loop order in case of constraints on patch occurence - const unsigned int - X = (unsigned int)cimg::round(cimg::rand(loop_order._width - 1.,&rng)), - Y = (unsigned int)cimg::round(cimg::rand(loop_order._height - 1.,&rng)), - Z = loop_order._depth>1?(unsigned int)cimg::round(cimg::rand(loop_order._depth - 1.,&rng)):0U; - cimg::swap(loop_order(x,y,z,0),loop_order(X,Y,Z,0)); - cimg::swap(loop_order(x,y,z,1),loop_order(X,Y,Z,1)); - if (loop_order._spectrum>2) cimg::swap(loop_order(x,y,z,2),loop_order(X,Y,Z,2)); - } - } - const int - psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1, - psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1, - psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1; - - if (_depth>1 || patch_image._depth>1) { // 3D version - - // Initialize correspondence map. - if (guide) cimg_forXYZ(*this,x,y,z) { // User-defined initialization - const int - cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1, - cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1, - cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1, - u = cimg::cut((int)guide(x,y,z,0),cx1,patch_image.width() - 1 - cx2), - v = cimg::cut((int)guide(x,y,z,1),cy1,patch_image.height() - 1 - cy2), - w = cimg::cut((int)guide(x,y,z,2),cz1,patch_image.depth() - 1 - cz2); - map(x,y,z,0) = u; - map(x,y,z,1) = v; - map(x,y,z,2) = w; - score(x,y,z) = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - x - cx1,y - cy1,z - cz1, - u - cx1,v - cy1,w - cz1, - u,v,w,0,cimg::type<float>::inf()); - } else cimg_forXYZ(*this,x,y,z) { // Random initialization - const int - cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1, - cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1, - cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1, - u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)), - v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)), - w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2,&rng)); - map(x,y,z,0) = u; - map(x,y,z,1) = v; - map(x,y,z,2) = w; - score(x,y,z) = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - x - cx1,y - cy1,z - cz1, - u - cx1,v - cy1,w - cz1, - u,v,w,0,cimg::type<float>::inf()); - } - - // Start iteration loop. - cimg_abort_init; - for (unsigned int iter = 0; iter<nb_iterations; ++iter) { - cimg_abort_test; - const bool is_odd = iter%2; - occ.fill(0); - - cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 && - iter<nb_iterations-2)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for collapse(2)) - cimg_forXYZ(*this,X,Y,Z) { - const int - _x = is_odd?width() - 1 - X:X, - _y = is_odd?height() - 1 - Y:Y, - _z = is_odd?depth() - 1 - Z:Z; - int x, y, z; - if (occ_penalization) { - x = loop_order(_x,_y,_z,0); - y = loop_order(_x,_y,_z,1); - if (loop_order._spectrum>2) z = loop_order(_x,_y,_z,2); else z = _z; - } else { x = _x; y = _y; z = _z; } - - if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue; - const int - cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1, - cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1, - cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1, - xp = x - cx1, - yp = y - cy1, - zp = z - cz1; - - int best_u = map(x,y,z,0), best_v = map(x,y,z,1), best_w = map(x,y,z,2), u, v, w; - const float best_score0 = score(x,y,z); - float best_score = best_score0, s; - - // Propagation. - if (x>0) { // Compare with left neighbor - u = map(x - 1,y,z,0); - v = map(x - 1,y,z,1); - w = map(x - 1,y,z,2); - if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 && - v>=cy1 && v<patch_image.height() - cy2 && - w>=cz1 && w<patch_image.depth() - cz2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1, - u,v,w,occ_penalization,best_score); - if (s<best_score) { best_u = u + 1; best_v = v; best_w = w; best_score = s; } - } - } - if (y>0) { // Compare with up neighbor - u = map(x,y - 1,z,0); - v = map(x,y - 1,z,1); - w = map(x,y - 1,z,2); - if (u>=cx1 && u<patch_image.width() - cx2 && - v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 && - w>=cz1 && w<patch_image.depth() - cz2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1, - u,v,w,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v + 1; best_w = w; best_score = s; } - } - } - if (z>0) { // Compare with backward neighbor - u = map(x,y,z - 1,0); - v = map(x,y,z - 1,1); - w = map(x,y,z - 1,2); - if (u>=cx1 && u<patch_image.width() - cx2 && - v>=cy1 && v<patch_image.height() - cy2 && - w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1, - u,v,w,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v; best_w = w + 1; best_score = s; } - } - } - if (x<width() - 1) { // Compare with right neighbor - u = map(x + 1,y,z,0); - v = map(x + 1,y,z,1); - w = map(x + 1,y,z,2); - if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 && - v>=cy1 && v<patch_image.height() - cy2 && - w>=cz1 && w<patch_image.depth() - cz2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1, - u,v,w,occ_penalization,best_score); - if (s<best_score) { best_u = u - 1; best_v = v; best_w = w; best_score = s; } - } - } - if (y<height() - 1) { // Compare with bottom neighbor - u = map(x,y + 1,z,0); - v = map(x,y + 1,z,1); - w = map(x,y + 1,z,2); - if (u>=cx1 && u<patch_image.width() - cx2 && - v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 && - w>=cz1 && w<patch_image.depth() - cz2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1, - u,v,w,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v - 1; best_w = w; best_score = s; } - } - } - if (z<depth() - 1) { // Compare with forward neighbor - u = map(x,y,z + 1,0); - v = map(x,y,z + 1,1); - w = map(x,y,z + 1,2); - if (u>=cx1 && u<patch_image.width() - cx2 && - v>=cy1 && v<patch_image.height() - cy2 && - w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1, - u,v,w,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v; best_w = w - 1; best_score = s; } - } - } - - // Randomization. - float - dw = (float)patch_image.width(), - dh = (float)patch_image.height(), - dd = (float)patch_image.depth(); - for (unsigned int i = 0; i<nb_randoms; ++i) { - u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw), - std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng)); - v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh), - std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng)); - w = (int)cimg::round(cimg::rand(std::max((float)cz1,best_w - dd), - std::min(patch_image.depth() - 1.f - cz2,best_w + dd),&rng)); - if (u!=best_u || v!=best_v || w!=best_w) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height,patch_depth, - xp,yp,zp,u - cx1,v - cy1,w - cz1, - u,v,w,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v; best_w = w; best_score = s; } - dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f); dd = std::max(5.f,dd*0.5f); - } - } - - if (best_score<best_score0) { - map(x,y,z,0) = best_u; - map(x,y,z,1) = best_v; - map(x,y,z,2) = best_w; - score(x,y,z) = best_score; - } - if (occ_penalization!=0) cimg_pragma_openmp(atomic) ++occ(best_u,best_v,best_w); - } - cimg::srand(rng); - } - } - - } else { // 2D version - - // Initialize correspondence map. - if (guide) cimg_forXY(*this,x,y) { // User-defined initialization - const int - cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1, - cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1, - u = cimg::cut((int)guide(x,y,0),cx1,patch_image.width() - 1 - cx2), - v = cimg::cut((int)guide(x,y,1),cy1,patch_image.height() - 1 - cy2); - map(x,y,0) = u; - map(x,y,1) = v; - score(x,y) = _matchpatch(*this,patch_image,occ,patch_width,patch_height, - x - cx1,y - cy1,u - cx1,v - cy1, - u,v,0,cimg::type<float>::inf()); - } else cimg_forXY(*this,x,y) { // Random initialization - const int - cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1, - cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1, - u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)), - v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)); - map(x,y,0) = u; - map(x,y,1) = v; - score(x,y) = _matchpatch(*this,patch_image,occ,patch_width,patch_height, - x - cx1,y - cy1,u - cx1,v - cy1, - u,v,0,cimg::type<float>::inf()); - } - - // Start iteration loop. - for (unsigned int iter = 0; iter<nb_iterations; ++iter) { - const bool is_odd = iter%2; - occ.fill(0); - - cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 && - iter<nb_iterations-2)) { - ulongT rng = (cimg::_rand(),cimg::rng()); -#ifdef cimg_use_openmp - rng+=omp_get_thread_num(); -#endif - cimg_pragma_openmp(for) - cimg_forXY(*this,X,Y) { - const int - _x = is_odd?width() - 1 - X:X, - _y = is_odd?height() - 1 - Y:Y; - int x, y; - if (occ_penalization) { - x = loop_order(_x,_y,0); - y = loop_order(_x,_y,1); - } else { x = _x; y = _y; } - - if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue; - const int - cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1, - cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1, - xp = x - cx1, - yp = y - cy1; - - int best_u = map(x,y,0), best_v = map(x,y,1), u, v; - const float best_score0 = score(x,y); - float best_score = best_score0, s; - - // Propagation. - if (x>0) { // Compare with left neighbor - u = map(x - 1,y,0); - v = map(x - 1,y,1); - if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 && - v>=cy1 && v<patch_image.height() - cy2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height, - xp,yp,u + 1 - cx1,v - cy1, - u,v,occ_penalization,best_score); - if (s<best_score) { best_u = u + 1; best_v = v; best_score = s; } - } - } - if (y>0) { // Compare with up neighbor - u = map(x,y - 1,0); - v = map(x,y - 1,1); - if (u>=cx1 && u<patch_image.width() - cx2 && - v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height, - xp,yp,u - cx1,v + 1 - cy1, - u,v,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v + 1; best_score = s; } - } - } - if (x<width() - 1) { // Compare with right neighbor - u = map(x + 1,y,0); - v = map(x + 1,y,1); - if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 && - v>=cy1 && v<patch_image.height() - cy2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height, - xp,yp,u - 1 - cx1,v - cy1, - u,v,occ_penalization,best_score); - if (s<best_score) { best_u = u - 1; best_v = v; best_score = s; } - } - } - if (y<height() - 1) { // Compare with bottom neighbor - u = map(x,y + 1,0); - v = map(x,y + 1,1); - if (u>=cx1 && u<patch_image.width() - cx2 && - v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height, - xp,yp,u - cx1,v - 1 - cy1, - u,v,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v - 1; best_score = s; } - } - } - - // Randomization. - float - dw = (float)patch_image.width(), - dh = (float)patch_image.height(); - for (unsigned int i = 0; i<nb_randoms; ++i) { - u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw), - std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng)); - v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh), - std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng)); - if (u!=best_u || v!=best_v) { - s = _matchpatch(*this,patch_image,occ,patch_width,patch_height, - xp,yp,u - cx1,v - cy1, - u,v,occ_penalization,best_score); - if (s<best_score) { best_u = u; best_v = v; best_score = s; } - dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f); - } - } - - if (best_score<best_score0) { - map(x,y,0) = best_u; - map(x,y,1) = best_v; - score(x,y) = best_score; - } - if (occ_penalization!=0) cimg_pragma_openmp(atomic) ++occ(best_u,best_v); - } - } - cimg::srand(rng); - } - } - if (is_matching_score) score.move_to(matching_score); - return map; - } - - // Compute SSD between two patches in different images. - static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<uintT>& occ, - const unsigned int psizew, const unsigned int psizeh, const unsigned int psized, - const int x1, const int y1, const int z1, - const int x2, const int y2, const int z2, - const int xc, const int yc, const int zc, - const float occ_penalization, - const float max_score) { // 3D version - const T *p1 = img1.data(x1,y1,z1), *p2 = img2.data(x2,y2,z2); - const ulongT - offx1 = (ulongT)img1._width - psizew, - offx2 = (ulongT)img2._width - psizew, - offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width, - offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width, - offz1 = (ulongT)img1._width*img1._height*img1._depth - (ulongT)psized*img1._width*img1._height, - offz2 = (ulongT)img2._width*img2._height*img2._depth - (ulongT)psized*img2._width*img2._height; - float ssd = 0; - cimg_forC(img1,c) { - for (unsigned int k = 0; k<psized; ++k) { - for (unsigned int j = 0; j<psizeh; ++j) { - for (unsigned int i = 0; i<psizew; ++i) - ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++)); - if (ssd>max_score) return max_score; - p1+=offx1; p2+=offx2; - } - p1+=offy1; p2+=offy2; - } - p1+=offz1; p2+=offz2; - } - return occ_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + occ_penalization*occ(xc,yc,zc)); - } - - static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<uintT>& occ, - const unsigned int psizew, const unsigned int psizeh, - const int x1, const int y1, - const int x2, const int y2, - const int xc, const int yc, - const float occ_penalization, - const float max_score) { // 2D version - const T *p1 = img1.data(x1,y1), *p2 = img2.data(x2,y2); - const ulongT - offx1 = (ulongT)img1._width - psizew, - offx2 = (ulongT)img2._width - psizew, - offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width, - offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width; - float ssd = 0; - cimg_forC(img1,c) { - for (unsigned int j = 0; j<psizeh; ++j) { - for (unsigned int i = 0; i<psizew; ++i) - ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++)); - if (ssd>max_score) return max_score; - p1+=offx1; p2+=offx2; - } - p1+=offy1; p2+=offy2; - } - return occ_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + occ_penalization*occ(xc,yc)); - } - - //! Compute Euclidean distance function to a specified value. - /** - \param value Reference value. - \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>. - \note - The distance transform implementation has been submitted by A. Meijster, and implements - the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink, - "A general algorithm for computing distance transforms in linear time.", - In: Mathematical Morphology and its Applications to Image and Signal Processing, - J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.' - The submitted code has then been modified to fit CImg coding style and constraints. - **/ - CImg<T>& distance(const T& value, const unsigned int metric=2) { - if (is_empty()) return *this; - if (cimg::type<Tint>::string()!=cimg::type<T>::string()) // For datatype < int - return CImg<Tint>(*this,false).distance((Tint)value,metric). - cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this); - bool is_value = false; - cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning) - if (!is_value) return fill(cimg::type<T>::max()); - switch (metric) { - case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev - case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan - case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean - default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean - } - return *this; - } - - //! Compute distance to a specified value \newinstance. - CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const { - return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric); - } - - static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) { - return (u*u - i*i + g[u] - g[i])/(2*(u - i)); - } - - static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) { - return (x - i)*(x - i) + g[i]; - } - - static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) { - return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2); - } - - static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) { - return (x<i?i - x:x - i) + g[i]; - } - - static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) { - const longT h = (i + u)/2; - if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; } - return h<u - g[i]?h:u - g[i]; - } - - static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) { - const longT d = x<i?i - x:x - i; - return d<g[i]?g[i]:d; - } - - static void _distance_scan(const unsigned int len, - const longT *const g, - longT (*const sep)(const longT, const longT, const longT *const), - longT (*const f)(const longT, const longT, const longT *const), - longT *const s, - longT *const t, - longT *const dt) { - longT q = s[0] = t[0] = 0; - for (int u = 1; u<(int)len; ++u) { // Forward scan - while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; } - if (q<0) { q = 0; s[0] = u; } - else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }} - } - for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan - } - - CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const), - longT (*const f)(const longT, const longT, const longT *const)) { - // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why. -#define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9) - - const ulongT wh = (ulongT)_width*_height; -#if defined(cimg_use_openmp) && !cimg_is_gcc49x - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) -#endif - cimg_forC(*this,c) { - CImg<longT> g(_width), dt(_width), s(_width), t(_width); - CImg<T> img = get_shared_channel(c); -#if defined(cimg_use_openmp) && !cimg_is_gcc49x - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && - _height*_depth>=16) - firstprivate(g,dt,s,t)) -#endif - cimg_forYZ(*this,y,z) { // Over X-direction - cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh); - _distance_scan(_width,g,sep,f,s,t,dt); - cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x]; - } - if (_height>1) { - g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height); -#if defined(cimg_use_openmp) && !cimg_is_gcc49x - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && - _width*_depth>=16) - firstprivate(g,dt,s,t)) -#endif - cimg_forXZ(*this,x,z) { // Over Y-direction - cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh); - _distance_scan(_height,g,sep,f,s,t,dt); - cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y]; - } - } - if (_depth>1) { - g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth); -#if defined(cimg_use_openmp) && !cimg_is_gcc49x - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && - _width*_height>=16) - firstprivate(g,dt,s,t)) -#endif - cimg_forXY(*this,x,y) { // Over Z-direction - cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh); - _distance_scan(_depth,g,sep,f,s,t,dt); - cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z]; - } - } - } - return *this; - } - - //! Compute chamfer distance to a specified value, with a custom metric. - /** - \param value Reference value. - \param metric_mask Metric mask. - \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé. - **/ - template<typename t> - CImg<T>& distance(const T& value, const CImg<t>& metric_mask) { - if (is_empty()) return *this; - bool is_value = false; - cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999; - if (!is_value) return fill(cimg::type<T>::max()); - const ulongT wh = (ulongT)_width*_height; - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - CImg<T> img = get_shared_channel(c); - cimg_pragma_openmp(parallel for collapse(3) - cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024)) - cimg_forXYZ(metric_mask,dx,dy,dz) { - const t weight = metric_mask(dx,dy,dz); - if (weight) { - for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan - for (int y = dy , ny = 0; y<height(); ++y,++ny) { - for (int x = dx, nx = 0; x<width(); ++x,++nx) { - const T dd = img(nx,ny,nz,0,wh) + weight; - if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd; - } - } - } - for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan - for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) { - for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) { - const T dd = img(nx,ny,nz,0,wh) + weight; - if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd; - } - } - } - } - } - } - return *this; - } - - //! Compute chamfer distance to a specified value, with a custom metric \newinstance. - template<typename t> - CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const { - return CImg<Tfloat>(*this,false).distance(value,metric_mask); - } - - //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm). - /** - \param value Reference value. - \param metric Field of distance potentials. - \param is_high_connectivity Tells if the algorithm uses low or high connectivity. - \param[out] return_path An image containing the nodes of the minimal path. - **/ - template<typename t, typename to> - CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity, - CImg<to>& return_path) { - return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); - } - - //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance. - template<typename t, typename to> - CImg<typename cimg::superset<t,long>::type> - get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity, - CImg<to>& return_path) const { - if (is_empty()) return return_path.assign(); - if (!is_sameXYZ(metric)) - throw CImgArgumentException(_cimg_instance - "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) " - "have incompatible dimensions.", - cimg_instance, - metric._width,metric._height,metric._depth,metric._spectrum); - typedef typename cimg::superset<t,long>::type td; // Type used for computing cumulative distances - CImg<td> result(_width,_height,_depth,_spectrum), Q; - CImg<boolT> is_queued(_width,_height,_depth,1); - if (return_path) return_path.assign(_width,_height,_depth,_spectrum); - - cimg_forC(*this,c) { - const CImg<T> img = get_shared_channel(c); - const CImg<t> met = metric.get_shared_channel(c%metric._spectrum); - CImg<td> res = result.get_shared_channel(c); - CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>(); - unsigned int sizeQ = 0; - - // Detect initial seeds. - is_queued.fill(0); - cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) { - Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z); - res(x,y,z) = 0; - if (path) path(x,y,z) = (to)0; - } - - // Start distance propagation. - while (sizeQ) { - - // Get and remove point with minimal potential from the queue. - const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); - const td P = (td)-Q(0,0); - Q._priority_queue_remove(sizeQ); - - // Update neighbors. - td npot = 0; - if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) { - res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2; - } - if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) { - res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1; - } - if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) { - res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8; - } - if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) { - res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4; - } - if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) { - res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32; - } - if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) { - res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16; - } - - if (is_high_connectivity) { - const float sqrt2 = std::sqrt(2.f), sqrt3 = std::sqrt(3.f); - - // Diagonal neighbors on slice z. - if (x - 1>=0 && y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) { - res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10; - } - if (x + 1<width() && y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) { - res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9; - } - if (x - 1>=0 && y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) { - res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6; - } - if (x + 1<width() && y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) { - res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5; - } - - if (z - 1>=0) { // Diagonal neighbors on slice z - 1 - if (x - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) { - res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34; - } - if (x + 1<width() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) { - res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33; - } - if (y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) { - res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40; - } - if (y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) { - res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36; - } - if (x - 1>=0 && y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)), - x - 1,y - 1,z - 1)) { - res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42; - } - if (x + 1<width() && y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)), - x + 1,y - 1,z - 1)) { - res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41; - } - if (x - 1>=0 && y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)), - x - 1,y + 1,z - 1)) { - res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38; - } - if (x + 1<width() && y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)), - x + 1,y + 1,z - 1)) { - res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37; - } - } - - if (z + 1<depth()) { // Diagonal neighbors on slice z + 1 - if (x - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) { - res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18; - } - if (x + 1<width() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) { - res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17; - } - if (y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) { - res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24; - } - if (y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) { - res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20; - } - if (x - 1>=0 && y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)), - x - 1,y - 1,z + 1)) { - res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26; - } - if (x + 1<width() && y - 1>=0 && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)), - x + 1,y - 1,z + 1)) { - res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25; - } - if (x - 1>=0 && y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)), - x - 1,y + 1,z + 1)) { - res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22; - } - if (x + 1<width() && y + 1<height() && - Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)), - x + 1,y + 1,z + 1)) { - res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21; - } - } - } - } - } - return result; - } - - //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading. - template<typename t> - CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, - const bool is_high_connectivity=false) { - return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this); - } - - //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. - template<typename t> - CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric, - const bool is_high_connectivity=false) const { - CImg<T> return_path; - return get_distance_dijkstra(value,metric,is_high_connectivity,return_path); - } - - //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). - /** - \param value Reference value. - \param metric Field of distance potentials. - **/ - template<typename t> - CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) { - return get_distance_eikonal(value,metric).move_to(*this); - } - - //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). - template<typename t> - CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const { - if (is_empty()) return *this; - if (!is_sameXYZ(metric)) - throw CImgArgumentException(_cimg_instance - "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have " - "incompatible dimensions.", - cimg_instance, - metric._width,metric._height,metric._depth,metric._spectrum); - CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q; - CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen - - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state)) - cimg_forC(*this,c) { - const CImg<T> img = get_shared_channel(c); - const CImg<t> met = metric.get_shared_channel(c%metric._spectrum); - CImg<Tfloat> res = result.get_shared_channel(c); - unsigned int sizeQ = 0; - state.fill(-1); - - // Detect initial seeds. - Tfloat *ptr1 = res._data; char *ptr2 = state._data; - cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; } - - // Initialize seeds neighbors. - ptr2 = state._data; - cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) { - if (x - 1>=0 && state(x - 1,y,z)==-1) { - const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z); - } - if (x + 1<width() && state(x + 1,y,z)==-1) { - const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z); - } - if (y - 1>=0 && state(x,y - 1,z)==-1) { - const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z); - } - if (y + 1<height() && state(x,y + 1,z)==-1) { - const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z); - } - if (z - 1>=0 && state(x,y,z - 1)==-1) { - const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1); - } - if (z + 1<depth() && state(x,y,z + 1)==-1) { - const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1); - Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1); - } - } - - // Propagate front. - while (sizeQ) { - int x = -1, y = -1, z = -1; - while (sizeQ && x<0) { - x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3); - Q._priority_queue_remove(sizeQ); - if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1; - } - if (x>=0) { - if (x - 1>=0 && state(x - 1,y,z)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); - if (dist<res(x - 1,y,z)) { - res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z); - } - } - if (x + 1<width() && state(x + 1,y,z)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z); - if (dist<res(x + 1,y,z)) { - res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z); - } - } - if (y - 1>=0 && state(x,y - 1,z)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); - if (dist<res(x,y - 1,z)) { - res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z); - } - } - if (y + 1<height() && state(x,y + 1,z)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z); - if (dist<res(x,y + 1,z)) { - res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z); - } - } - if (z - 1>=0 && state(x,y,z - 1)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); - if (dist<res(x,y,z - 1)) { - res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1); - } - } - if (z + 1<depth() && state(x,y,z + 1)!=1) { - const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1); - if (dist<res(x,y,z + 1)) { - res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1); - } - } - } - } - } - return result; - } - - // Locally solve eikonal equation. - Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P, - const int x=0, const int y=0, const int z=0) const { - const Tfloat M = (Tfloat)cimg::type<T>::max(); - T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M); - Tfloat root = 0; - if (_depth>1) { // 3D - T - T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M), - T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M); - if (T1>T2) cimg::swap(T1,T2); - if (T2>T3) cimg::swap(T2,T3); - if (T1>T2) cimg::swap(T1,T2); - if (P<=0) return (Tfloat)T1; - if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root)) - return std::max((Tfloat)T3,root); - if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root)) - return std::max((Tfloat)T2,root); - return P + T1; - } else if (_height>1) { // 2D - T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M); - if (T1>T2) cimg::swap(T1,T2); - if (P<=0) return (Tfloat)T1; - if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root)) - return std::max((Tfloat)T2,root); - return P + T1; - } else { // 1D - if (P<=0) return (Tfloat)T1; - return P + T1; - } - return 0; - } - - // Find max root of a 2nd-order polynomial. - static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) { - const Tfloat delta = b*b - 4*a*c; - if (delta<0) return false; - root = 0.5f*(-b + std::sqrt(delta))/a; - return true; - } - - // Insert new point in heap. - template<typename t> - void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value, - const unsigned int x, const unsigned int y, const unsigned int z) { - if (state(x,y,z)>0) return; - state(x,y,z) = 0; - if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } - (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z; - for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos + 1)/2 - 1,0); pos = par) { - cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1)); - cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3)); - } - } - - //! Compute distance function to 0-valued isophotes, using the Eikonal PDE. - /** - \param nb_iterations Number of PDE iterations. - \param band_size Size of the narrow band. - \param time_step Time step of the PDE iterations. - **/ - CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) { - if (is_empty()) return *this; - CImg<Tfloat> velocity(*this,false); - for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) { - Tfloat *ptrd = velocity._data, veloc_max = 0; - if (_depth>1) { // 3D - CImg_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) { - const Tfloat - gx = (Incc - Ipcc)/2, - gy = (Icnc - Icpc)/2, - gz = (Iccn - Iccp)/2, - sgn = -cimg::sign(Iccc), - ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc), - iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc), - iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp), - ng = 1e-5f + cimg::hypot(gx,gy,gz), - ngx = gx/ng, - ngy = gy/ng, - ngz = gz/ng, - veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1); - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } else *(ptrd++) = 0; - } else { // 2D version - CImg_3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) { - const Tfloat - gx = (Inc - Ipc)/2, - gy = (Icn - Icp)/2, - sgn = -cimg::sign(Icc), - ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc), - iy = gy*sgn>0?(Icn - Icc):(Icc - Icp), - ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)), - ngx = gx/ng, - ngy = gy/ng, - veloc = sgn*(ngx*ix + ngy*iy - 1); - *(ptrd++) = veloc; - if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; - } else *(ptrd++) = 0; - } - if (veloc_max>0) *this+=(velocity*=time_step/veloc_max); - } - return *this; - } - - //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance. - CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, - const float time_step=0.5f) const { - return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step); - } - - //! Compute Haar multiscale wavelet transform. - /** - \param axis Axis considered for the transform. - \param invert Set inverse of direct transform. - \param nb_scales Number of scales used for the transform. - **/ - CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) { - return get_haar(axis,invert,nb_scales).move_to(*this); - } - - //! Compute Haar multiscale wavelet transform \newinstance. - CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { - if (is_empty() || !nb_scales) return +*this; - CImg<Tfloat> res; - const Tfloat sqrt2 = std::sqrt(2.f); - if (nb_scales==1) { - switch (cimg::lowercase(axis)) { // Single scale transform - case 'x' : { - const unsigned int w = _width/2; - if (w) { - if ((w%2) && w!=1) - throw CImgInstanceException(_cimg_instance - "haar(): Sub-image width %u is not even.", - cimg_instance, - w); - - res.assign(_width,_height,_depth,_spectrum); - if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X - for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) { - const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c); - res(x2++,y,z,c) = (val0 - val1)/sqrt2; - res(x2++,y,z,c) = (val0 + val1)/sqrt2; - } - } else cimg_forYZC(*this,y,z,c) { // Direct transform along X - for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) { - const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c); - res(x,y,z,c) = (val0 + val1)/sqrt2; - res(xw,y,z,c) = (val1 - val0)/sqrt2; - } - } - } else return *this; - } break; - case 'y' : { - const unsigned int h = _height/2; - if (h) { - if ((h%2) && h!=1) - throw CImgInstanceException(_cimg_instance - "haar(): Sub-image height %u is not even.", - cimg_instance, - h); - - res.assign(_width,_height,_depth,_spectrum); - if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y - for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { - const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c); - res(x,y2++,z,c) = (val0 - val1)/sqrt2; - res(x,y2++,z,c) = (val0 + val1)/sqrt2; - } - } else cimg_forXZC(*this,x,z,c) { - for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y - const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c); - res(x,y,z,c) = (val0 + val1)/sqrt2; - res(x,yh,z,c) = (val1 - val0)/sqrt2; - } - } - } else return *this; - } break; - case 'z' : { - const unsigned int d = _depth/2; - if (d) { - if ((d%2) && d!=1) - throw CImgInstanceException(_cimg_instance - "haar(): Sub-image depth %u is not even.", - cimg_instance, - d); - - res.assign(_width,_height,_depth,_spectrum); - if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z - for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { - const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c); - res(x,y,z2++,c) = (val0 - val1)/sqrt2; - res(x,y,z2++,c) = (val0 + val1)/sqrt2; - } - } else cimg_forXYC(*this,x,y,c) { - for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z - const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c); - res(x,y,z,c) = (val0 + val1)/sqrt2; - res(x,y,zd,c) = (val1 - val0)/sqrt2; - } - } - } else return *this; - } break; - default : - throw CImgArgumentException(_cimg_instance - "haar(): Invalid specified axis '%c' " - "(should be { x | y | z }).", - cimg_instance, - axis); - } - } else { // Multi-scale version - if (invert) { - res.assign(*this,false); - switch (cimg::lowercase(axis)) { - case 'x' : { - unsigned int w = _width; - for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2; - for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1)); - } break; - case 'y' : { - unsigned int h = _width; - for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2; - for (h = h?h:1; h<=_height; h*=2) res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',true,1)); - } break; - case 'z' : { - unsigned int d = _depth; - for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2; - for (d = d?d:1; d<=_depth; d*=2) - res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1)); - } break; - default : - throw CImgArgumentException(_cimg_instance - "haar(): Invalid specified axis '%c' " - "(should be { x | y | z }).", - cimg_instance, - axis); - } - } else { // Direct transform - res = get_haar(axis,false,1); - switch (cimg::lowercase(axis)) { - case 'x' : { - for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2) - res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1)); - } break; - case 'y' : { - for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2) - res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1)); - } break; - case 'z' : { - for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2) - res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1)); - } break; - default : - throw CImgArgumentException(_cimg_instance - "haar(): Invalid specified axis '%c' " - "(should be { x | y | z }).", - cimg_instance, - axis); - } - } - } - return res; - } - - //! Compute Haar multiscale wavelet transform \overloading. - /** - \param invert Set inverse of direct transform. - \param nb_scales Number of scales used for the transform. - **/ - CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) { - return get_haar(invert,nb_scales).move_to(*this); - } - - //! Compute Haar multiscale wavelet transform \newinstance. - CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const { - CImg<Tfloat> res; - if (nb_scales==1) { // Single scale transform - if (_width>1) get_haar('x',invert,1).move_to(res); - if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); } - if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); } - if (res) return res; - } else { // Multi-scale transform - if (invert) { // Inverse transform - res.assign(*this,false); - if (_width>1) { - if (_height>1) { - if (_depth>1) { - unsigned int w = _width, h = _height, d = _depth; - for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; } - for (w = w?w:1, h = h?h:1, d = d?d:1; w<=_width && h<=_height && d<=_depth; w*=2, h*=2, d*=2) - res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1)); - } else { - unsigned int w = _width, h = _height; - for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; } - for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2) - res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1)); - } - } else { - if (_depth>1) { - unsigned int w = _width, d = _depth; - for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; } - for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2) - res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1)); - } else { - unsigned int w = _width; - for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2; - for (w = w?w:1; w<=_width; w*=2) - res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1)); - } - } - } else { - if (_height>1) { - if (_depth>1) { - unsigned int h = _height, d = _depth; - for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; } - for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2) - res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1)); - } else { - unsigned int h = _height; - for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2; - for (h = h?h:1; h<=_height; h*=2) - res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1)); - } - } else { - if (_depth>1) { - unsigned int d = _depth; - for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2; - for (d = d?d:1; d<=_depth; d*=2) - res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1)); - } else return *this; - } - } - } else { // Direct transform - res = get_haar(false,1); - if (_width>1) { - if (_height>1) { - if (_depth>1) - for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales; - ++s, w/=2, h/=2, d/=2) - res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1)); - else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2) - res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1)); - } else { - if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2) - res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1)); - else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2) - res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1)); - } - } else { - if (_height>1) { - if (_depth>1) - for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2) - res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1)); - else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2) - res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1)); - } else { - if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2) - res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1)); - else return *this; - } - } - } - return res; - } - return *this; - } - - //! Compute 1D Fast Fourier Transform, along a specified axis. - /** - \param axis Axis along which the FFT is computed. - \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. - **/ - CImgList<Tfloat> get_FFT(const char axis, const bool is_invert=false) const { - CImgList<Tfloat> res(*this,CImg<Tfloat>()); - CImg<Tfloat>::FFT(res[0],res[1],axis,is_invert); - return res; - } - - //! Compute n-d Fast Fourier Transform. - /* - \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. - **/ - CImgList<Tfloat> get_FFT(const bool is_invert=false) const { - CImgList<Tfloat> res(*this,CImg<Tfloat>()); - CImg<Tfloat>::FFT(res[0],res[1],is_invert); - return res; - } - - //! Compute 1D Fast Fourier Transform, along a specified axis. - /** - \param[in,out] real Real part of the pixel values. - \param[in,out] imag Imaginary part of the pixel values. - \param axis Axis along which the FFT is computed. - \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. - **/ - static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_invert=false) { - if (!real) - throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.", - pixel_type()); - - if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); - if (!real.is_sameXYZC(imag)) - throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " - "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", - pixel_type(), - real._width,real._height,real._depth,real._spectrum,real._data, - imag._width,imag._height,imag._depth,imag._spectrum,imag._data); -#ifdef cimg_use_fftw3 - cimg::mutex(12); - fftw_complex *data_in; - fftw_plan data_plan; - - switch (cimg::lowercase(axis)) { - case 'x' : { // Fourier along X, using FFTW library - data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width); - if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " - "for computing FFT of image (%u,%u,%u,%u) along the X-axis.", - pixel_type(), - cimg::strbuffersize(sizeof(fftw_complex)*real._width), - real._width,real._height,real._depth,real._spectrum); - - data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); - cimg_forYZC(real,y,z,c) { - T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c); - double *ptrd = (double*)data_in; - cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); } - fftw_execute(data_plan); - const unsigned int fact = real._width; - if (is_invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); } - else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); } - } - } break; - case 'y' : { // Fourier along Y, using FFTW library - data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height); - if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " - "for computing FFT of image (%u,%u,%u,%u) along the Y-axis.", - pixel_type(), - cimg::strbuffersize(sizeof(fftw_complex)*real._height), - real._width,real._height,real._depth,real._spectrum); - - data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); - const unsigned int off = real._width; - cimg_forXZC(real,x,z,c) { - T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c); - double *ptrd = (double*)data_in; - cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } - fftw_execute(data_plan); - const unsigned int fact = real._height; - if (is_invert) - cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } - else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } - } - } break; - case 'z' : { // Fourier along Z, using FFTW library - data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth); - if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " - "for computing FFT of image (%u,%u,%u,%u) along the Z-axis.", - pixel_type(), - cimg::strbuffersize(sizeof(fftw_complex)*real._depth), - real._width,real._height,real._depth,real._spectrum); - - data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); - const ulongT off = (ulongT)real._width*real._height; - cimg_forXYC(real,x,y,c) { - T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c); - double *ptrd = (double*)data_in; - cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } - fftw_execute(data_plan); - const unsigned int fact = real._depth; - if (is_invert) - cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } - else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } - } - } break; - default : - throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts " - "(%u,%u,%u,%u) " - "(should be { x | y | z }).", - pixel_type(),axis, - real._width,real._height,real._depth,real._spectrum); - } - fftw_destroy_plan(data_plan); - fftw_free(data_in); - cimg::mutex(12,0); -#else - switch (cimg::lowercase(axis)) { - case 'x' : { // Fourier along X, using built-in functions - const unsigned int N = real._width, N2 = N>>1; - if (((N - 1)&N) && N!=1) - throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " - "have non 2^N dimension along the X-axis.", - pixel_type(), - real._width,real._height,real._depth,real._spectrum); - - for (unsigned int i = 0, j = 0; i<N2; ++i) { - if (j>i) cimg_forYZC(real,y,z,c) { - cimg::swap(real(i,y,z,c),real(j,y,z,c)); - cimg::swap(imag(i,y,z,c),imag(j,y,z,c)); - if (j<N2) { - const unsigned int ri = N - 1 - i, rj = N - 1 - j; - cimg::swap(real(ri,y,z,c),real(rj,y,z,c)); - cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c)); - } - } - for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {} - } - for (unsigned int delta = 2; delta<=N; delta<<=1) { - const unsigned int delta2 = delta>>1; - for (unsigned int i = 0; i<N; i+=delta) { - float wr = 1, wi = 0; - const float - angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta), - ca = (float)std::cos(angle), - sa = (float)std::sin(angle); - for (unsigned int k = 0; k<delta2; ++k) { - const unsigned int j = i + k, nj = j + delta2; - cimg_forYZC(real,y,z,c) { - T &ir = real(j,y,z,c), &ii = imag(j,y,z,c), &nir = real(nj,y,z,c), &nii = imag(nj,y,z,c); - const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir); - nir = (T)(ir - tmpr); - nii = (T)(ii - tmpi); - ir+=(T)tmpr; - ii+=(T)tmpi; - } - const float nwr = wr*ca-wi*sa; - wi = wi*ca + wr*sa; - wr = nwr; - } - } - } - if (is_invert) { real/=N; imag/=N; } - } break; - case 'y' : { // Fourier along Y, using built-in functions - const unsigned int N = real._height, N2 = N>>1; - if (((N - 1)&N) && N!=1) - throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " - "have non 2^N dimension along the Y-axis.", - pixel_type(), - real._width,real._height,real._depth,real._spectrum); - - for (unsigned int i = 0, j = 0; i<N2; ++i) { - if (j>i) cimg_forXZC(real,x,z,c) { - cimg::swap(real(x,i,z,c),real(x,j,z,c)); - cimg::swap(imag(x,i,z,c),imag(x,j,z,c)); - if (j<N2) { - const unsigned int ri = N - 1 - i, rj = N - 1 - j; - cimg::swap(real(x,ri,z,c),real(x,rj,z,c)); - cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c)); - } - } - for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {} - } - for (unsigned int delta = 2; delta<=N; delta<<=1) { - const unsigned int delta2 = (delta>>1); - for (unsigned int i = 0; i<N; i+=delta) { - float wr = 1, wi = 0; - const float - angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta), - ca = (float)std::cos(angle), - sa = (float)std::sin(angle); - for (unsigned int k = 0; k<delta2; ++k) { - const unsigned int j = i + k, nj = j + delta2; - cimg_forXZC(real,x,z,c) { - T &ir = real(x,j,z,c), &ii = imag(x,j,z,c), &nir = real(x,nj,z,c), &nii = imag(x,nj,z,c); - const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir); - nir = (T)(ir - tmpr); - nii = (T)(ii - tmpi); - ir+=(T)tmpr; - ii+=(T)tmpi; - } - const float nwr = wr*ca-wi*sa; - wi = wi*ca + wr*sa; - wr = nwr; - } - } - } - if (is_invert) { real/=N; imag/=N; } - } break; - case 'z' : { // Fourier along Z, using built-in functions - const unsigned int N = real._depth, N2 = N>>1; - if (((N - 1)&N) && N!=1) - throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " - "have non 2^N dimension along the Z-axis.", - pixel_type(), - real._width,real._height,real._depth,real._spectrum); - - for (unsigned int i = 0, j = 0; i<N2; ++i) { - if (j>i) cimg_forXYC(real,x,y,c) { - cimg::swap(real(x,y,i,c),real(x,y,j,c)); - cimg::swap(imag(x,y,i,c),imag(x,y,j,c)); - if (j<N2) { - const unsigned int ri = N - 1 - i, rj = N - 1 - j; - cimg::swap(real(x,y,ri,c),real(x,y,rj,c)); - cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c)); - } - } - for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {} - } - for (unsigned int delta = 2; delta<=N; delta<<=1) { - const unsigned int delta2 = (delta>>1); - for (unsigned int i = 0; i<N; i+=delta) { - float wr = 1, wi = 0; - const float - angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta), - ca = (float)std::cos(angle), - sa = (float)std::sin(angle); - for (unsigned int k = 0; k<delta2; ++k) { - const unsigned int j = i + k, nj = j + delta2; - cimg_forXYC(real,x,y,c) { - T &ir = real(x,y,j,c), &ii = imag(x,y,j,c), &nir = real(x,y,nj,c), &nii = imag(x,y,nj,c); - const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir); - nir = (T)(ir - tmpr); - nii = (T)(ii - tmpi); - ir+=(T)tmpr; - ii+=(T)tmpi; - } - const float nwr = wr*ca-wi*sa; - wi = wi*ca + wr*sa; - wr = nwr; - } - } - } - if (is_invert) { real/=N; imag/=N; } - } break; - default : - throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts " - "(%u,%u,%u,%u) " - "(should be { x | y | z }).", - pixel_type(),axis, - real._width,real._height,real._depth,real._spectrum); - } -#endif - } - - //! Compute n-d Fast Fourier Transform. - /** - \param[in,out] real Real part of the pixel values. - \param[in,out] imag Imaginary part of the pixel values. - \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. - \param nb_threads Number of parallel threads used for the computation. - Use \c 0 to set this to the number of available cpus. - **/ - static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_invert=false, const unsigned int nb_threads=0) { - if (!real) - throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.", - pixel_type()); - - if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); - if (!real.is_sameXYZC(imag)) - throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " - "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", - pixel_type(), - real._width,real._height,real._depth,real._spectrum,real._data, - imag._width,imag._height,imag._depth,imag._spectrum,imag._data); - -#ifdef cimg_use_fftw3 - cimg::mutex(12); -#ifndef cimg_use_fftw3_singlethread - const unsigned int _nb_threads = nb_threads?nb_threads:cimg::nb_cpus(); - static int fftw_st = fftw_init_threads(); - cimg::unused(fftw_st); - fftw_plan_with_nthreads(_nb_threads); -#else - cimg::unused(nb_threads); -#endif - fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); - if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " - "for computing FFT of image (%u,%u,%u,%u).", - pixel_type(), - cimg::strbuffersize(sizeof(fftw_complex)*real._width* - real._height*real._depth*real._spectrum), - real._width,real._height,real._depth,real._spectrum); - - fftw_plan data_plan; - const ulongT w = (ulongT)real._width, wh = w*real._height, whd = wh*real._depth; - data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in, - is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); - cimg_forC(real,c) { - T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c); - double *ptrd = (double*)data_in; - for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1) - for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w) - for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) { - *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; - } - fftw_execute(data_plan); - ptrd = (double*)data_in; - ptrr = real.data(0,0,0,c); - ptri = imag.data(0,0,0,c); - if (!is_invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1) - for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w) - for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) { - *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++); - } - else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1) - for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w) - for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) { - *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd); - } - } - fftw_destroy_plan(data_plan); - fftw_free(data_in); -#ifndef cimg_use_fftw3_singlethread - fftw_cleanup_threads(); -#endif - cimg::mutex(12,0); -#else - cimg::unused(nb_threads); - if (real._depth>1) FFT(real,imag,'z',is_invert); - if (real._height>1) FFT(real,imag,'y',is_invert); - if (real._width>1) FFT(real,imag,'x',is_invert); -#endif - } - - //@} - //------------------------------------- - // - //! \name 3D Objects Management - //@{ - //------------------------------------- - - //! Shift 3D object's vertices. - /** - \param tx X-coordinate of the 3D displacement vector. - \param ty Y-coordinate of the 3D displacement vector. - \param tz Z-coordinate of the 3D displacement vector. - **/ - CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) { - if (_height!=3 || _depth>1 || _spectrum>1) - throw CImgInstanceException(_cimg_instance - "shift_object3d(): Instance is not a set of 3D vertices.", - cimg_instance); - - get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz; - return *this; - } - - //! Shift 3D object's vertices \newinstance. - CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const { - return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz); - } - - //! Shift 3D object's vertices, so that it becomes centered. - /** - \note The object center is computed as its barycenter. - **/ - CImg<T>& shift_object3d() { - if (_height!=3 || _depth>1 || _spectrum>1) - throw CImgInstanceException(_cimg_instance - "shift_object3d(): Instance is not a set of 3D vertices.", - cimg_instance); - - CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); - float - xm, xM = (float)xcoords.max_min(xm), - ym, yM = (float)ycoords.max_min(ym), - zm, zM = (float)zcoords.max_min(zm); - xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2; - return *this; - } - - //! Shift 3D object's vertices, so that it becomes centered \newinstance. - CImg<Tfloat> get_shift_object3d() const { - return CImg<Tfloat>(*this,false).shift_object3d(); - } - - //! Resize 3D object. - /** - \param sx Width of the 3D object's bounding box. - \param sy Height of the 3D object's bounding box. - \param sz Depth of the 3D object's bounding box. - **/ - CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) { - if (_height!=3 || _depth>1 || _spectrum>1) - throw CImgInstanceException(_cimg_instance - "resize_object3d(): Instance is not a set of 3D vertices.", - cimg_instance); - - CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); - float - xm, xM = (float)xcoords.max_min(xm), - ym, yM = (float)ycoords.max_min(ym), - zm, zM = (float)zcoords.max_min(zm); - if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } - if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } - if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } - return *this; - } - - //! Resize 3D object \newinstance. - CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { - return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz); - } - - //! Resize 3D object to unit size. - CImg<T> resize_object3d() { - if (_height!=3 || _depth>1 || _spectrum>1) - throw CImgInstanceException(_cimg_instance - "resize_object3d(): Instance is not a set of 3D vertices.", - cimg_instance); - - CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); - float - xm, xM = (float)xcoords.max_min(xm), - ym, yM = (float)ycoords.max_min(ym), - zm, zM = (float)zcoords.max_min(zm); - const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz); - if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; } - return *this; - } - - //! Resize 3D object to unit size \newinstance. - CImg<Tfloat> get_resize_object3d() const { - return CImg<Tfloat>(*this,false).resize_object3d(); - } - - //! Merge two 3D objects together. - /** - \param[in,out] primitives Primitives data of the current 3D object. - \param obj_vertices Vertices data of the additional 3D object. - \param obj_primitives Primitives data of the additional 3D object. - **/ - template<typename tf, typename tp, typename tff> - CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices, - const CImgList<tff>& obj_primitives) { - if (!obj_vertices || !obj_primitives) return *this; - if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1) - throw CImgInstanceException(_cimg_instance - "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a " - "set of 3D vertices.", - cimg_instance, - obj_vertices._width,obj_vertices._height, - obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data); - - if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); } - if (_height!=3 || _depth>1 || _spectrum>1) - throw CImgInstanceException(_cimg_instance - "append_object3d(): Instance is not a set of 3D vertices.", - cimg_instance); - - const unsigned int P = _width; - append(obj_vertices,'x'); - const unsigned int N = primitives._width; - primitives.insert(obj_primitives); - for (unsigned int i = N; i<primitives._width; ++i) { - CImg<tf> &p = primitives[i]; - switch (p.size()) { - case 1 : p[0]+=P; break; // Point - case 5 : p[0]+=P; p[1]+=P; break; // Sphere - case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment - case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle - case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle - } - } - return *this; - } - - //! Texturize primitives of a 3D object. - /** - \param[in,out] primitives Primitives data of the 3D object. - \param[in,out] colors Colors data of the 3D object. - \param texture Texture image to map to 3D object. - \param coords Texture-mapping coordinates. - **/ - template<typename tp, typename tc, typename tt, typename tx> - const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors, - const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const { - if (is_empty()) return *this; - if (_height!=3) - throw CImgInstanceException(_cimg_instance - "texturize_object3d(): image instance is not a set of 3D points.", - cimg_instance); - if (coords && (coords._width!=_width || coords._height!=2)) - throw CImgArgumentException(_cimg_instance - "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).", - cimg_instance, - coords._width,coords._height,coords._depth,coords._spectrum,coords._data); - CImg<intT> _coords; - if (!coords) { // If no texture coordinates specified, do a default XY-projection - _coords.assign(_width,2); - float - xmin, xmax = (float)get_shared_row(0).max_min(xmin), - ymin, ymax = (float)get_shared_row(1).max_min(ymin), - dx = xmax>xmin?xmax-xmin:1, - dy = ymax>ymin?ymax-ymin:1; - cimg_forX(*this,p) { - _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx); - _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy); - } - } else _coords = coords; - - int texture_ind = -1; - cimglist_for(primitives,l) { - CImg<tp> &p = primitives[l]; - const unsigned int siz = p.size(); - switch (siz) { - case 1 : { // Point - const unsigned int i0 = (unsigned int)p[0]; - const int x0 = _coords(i0,0), y0 = _coords(i0,1); - texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, - y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]); - } break; - case 2 : case 6 : { // Line - const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1]; - const int - x0 = _coords(i0,0), y0 = _coords(i0,1), - x1 = _coords(i1,0), y1 = _coords(i1,1); - if (texture_ind<0) colors[texture_ind=l].assign(texture,false); - else colors[l].assign(colors[texture_ind],true); - CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p); - } break; - case 3 : case 9 : { // Triangle - const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2]; - const int - x0 = _coords(i0,0), y0 = _coords(i0,1), - x1 = _coords(i1,0), y1 = _coords(i1,1), - x2 = _coords(i2,0), y2 = _coords(i2,1); - if (texture_ind<0) colors[texture_ind=l].assign(texture,false); - else colors[l].assign(colors[texture_ind],true); - CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p); - } break; - case 4 : case 12 : { // Quadrangle - const unsigned int - i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3]; - const int - x0 = _coords(i0,0), y0 = _coords(i0,1), - x1 = _coords(i1,0), y1 = _coords(i1,1), - x2 = _coords(i2,0), y2 = _coords(i2,1), - x3 = _coords(i3,0), y3 = _coords(i3,1); - if (texture_ind<0) colors[texture_ind=l].assign(texture,false); - else colors[l].assign(colors[texture_ind],true); - CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p); - } break; - } - } - return *this; - } - - //! Generate a 3D elevation of the image instance. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param[out] colors The returned list of the 3D object colors. - \param elevation The input elevation map. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - const CImg<float> img("reference.jpg"); - CImgList<unsigned int> faces3d; - CImgList<unsigned char> colors3d; - const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2); - CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d); - \endcode - \image html ref_elevation3d.jpg - **/ - template<typename tf, typename tc, typename te> - CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const { - if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1) - throw CImgArgumentException(_cimg_instance - "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) " - "have incompatible dimensions.", - cimg_instance, - elevation._width,elevation._height,elevation._depth, - elevation._spectrum,elevation._data); - if (is_empty()) return *this; - float m, M = (float)max_min(m); - if (M==m) ++M; - colors.assign(); - const unsigned int size_x1 = _width - 1, size_y1 = _height - 1; - for (unsigned int y = 0; y<size_y1; ++y) - for (unsigned int x = 0; x<size_x1; ++x) { - const unsigned char - r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)), - g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r), - b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r); - CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors); - } - const typename CImg<te>::_functor2d_int func(elevation); - return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height); - } - - //! Generate the 3D projection planes of the image instance. - /** - \param[out] primitives Primitives data of the returned 3D object. - \param[out] colors Colors data of the returned 3D object. - \param x0 X-coordinate of the projection point. - \param y0 Y-coordinate of the projection point. - \param z0 Z-coordinate of the projection point. - \param normalize_colors Tells if the created textures have normalized colors. - **/ - template<typename tf, typename tc> - CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors, - const unsigned int x0, const unsigned int y0, const unsigned int z0, - const bool normalize_colors=false) const { - float m = 0, M = 0, delta = 1; - if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); } - const unsigned int - _x0 = (x0>=_width)?_width - 1:x0, - _y0 = (y0>=_height)?_height - 1:y0, - _z0 = (z0>=_depth)?_depth - 1:z0; - CImg<tc> img_xy, img_xz, img_yz; - if (normalize_colors) { - ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy); - ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1). - move_to(img_xz); - ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1). - move_to(img_yz); - } else { - get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy); - get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz); - get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz); - } - CImg<floatT> points(12,3,1,1, - 0,_width - 1,_width - 1,0, 0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0, - 0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0, 0,_height - 1,_height - 1,0, - _z0,_z0,_z0,_z0, 0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1); - primitives.assign(); - CImg<tf>::vector(0,1,2,3,0,0,img_xy._width - 1,0,img_xy._width - 1,img_xy._height - 1,0,img_xy._height - 1). - move_to(primitives); - CImg<tf>::vector(4,5,6,7,0,0,img_xz._width - 1,0,img_xz._width - 1,img_xz._height - 1,0,img_xz._height - 1). - move_to(primitives); - CImg<tf>::vector(8,9,10,11,0,0,img_yz._width - 1,0,img_yz._width - 1,img_yz._height - 1,0,img_yz._height - 1). - move_to(primitives); - colors.assign(); - img_xy.move_to(colors); - img_xz.move_to(colors); - img_yz.move_to(colors); - return points; - } - - //! Generate a isoline of the image instance as a 3D object. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param isovalue The returned list of the 3D object colors. - \param size_x The number of subdivisions along the X-axis. - \param size_y The number of subdisivions along the Y-axis. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - const CImg<float> img("reference.jpg"); - CImgList<unsigned int> faces3d; - const CImg<float> points3d = img.get_isoline3d(faces3d,100); - CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d); - \endcode - \image html ref_isoline3d.jpg - **/ - template<typename tf> - CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue, - const int size_x=-100, const int size_y=-100) const { - if (_spectrum>1) - throw CImgInstanceException(_cimg_instance - "get_isoline3d(): Instance is not a scalar image.", - cimg_instance); - if (_depth>1) - throw CImgInstanceException(_cimg_instance - "get_isoline3d(): Instance is not a 2D image.", - cimg_instance); - primitives.assign(); - if (is_empty()) return *this; - CImg<floatT> vertices; - if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) { - const _functor2d_int func(*this); - vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height()); - } else { - const _functor2d_float func(*this); - vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y); - } - return vertices; - } - - //! Generate an isosurface of the image instance as a 3D object. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param isovalue The returned list of the 3D object colors. - \param size_x Number of subdivisions along the X-axis. - \param size_y Number of subdisivions along the Y-axis. - \param size_z Number of subdisivions along the Z-axis. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20); - CImgList<unsigned int> faces3d; - const CImg<float> points3d = img.get_isosurface3d(faces3d,100); - CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d); - \endcode - \image html ref_isosurface3d.jpg - **/ - template<typename tf> - CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue, - const int size_x=-100, const int size_y=-100, const int size_z=-100) const { - if (_spectrum>1) - throw CImgInstanceException(_cimg_instance - "get_isosurface3d(): Instance is not a scalar image.", - cimg_instance); - primitives.assign(); - if (is_empty()) return *this; - CImg<floatT> vertices; - if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) { - const _functor3d_int func(*this); - vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, - width(),height(),depth()); - } else { - const _functor3d_float func(*this); - vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, - size_x,size_y,size_z); - } - return vertices; - } - - //! Compute 3D elevation of a function as a 3D object. - /** - \param[out] primitives Primitives data of the resulting 3D object. - \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>. - \param x0 X-coordinate of the starting point. - \param y0 Y-coordinate of the starting point. - \param x1 X-coordinate of the ending point. - \param y1 Y-coordinate of the ending point. - \param size_x Resolution of the function along the X-axis. - \param size_y Resolution of the function along the Y-axis. - **/ - template<typename tf, typename tfunc> - static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func, - const float x0, const float y0, const float x1, const float y1, - const int size_x=256, const int size_y=256) { - const float - nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1, - nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0; - const unsigned int - _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100), - nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1, - _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), - nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1; - if (nsize_x<2 || nsize_y<2) - throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).", - pixel_type(), - nsize_x,nsize_y); - - CImg<floatT> vertices(nsize_x*nsize_y,3); - floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2); - for (unsigned int y = 0; y<nsize_y; ++y) { - const float Y = ny0 + y*(ny1-ny0)/nsize_y1; - for (unsigned int x = 0; x<nsize_x; ++x) { - const float X = nx0 + x*(nx1-nx0)/nsize_x1; - *(ptr_x++) = (float)x; - *(ptr_y++) = (float)y; - *(ptr_z++) = (float)func(X,Y); - } - } - primitives.assign(nsize_x1*nsize_y1,1,4); - for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) { - const unsigned int yw = y*nsize_x; - for (unsigned int x = 0; x<nsize_x1; ++x) { - const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x; - primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1); - } - } - return vertices; - } - - //! Compute 3D elevation of a function, as a 3D object \overloading. - template<typename tf> - static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression, - const float x0, const float y0, const float x1, const float y1, - const int size_x=256, const int size_y=256) { - const _functor2d_expr func(expression); - return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y); - } - - //! Compute 0-isolines of a function, as a 3D object. - /** - \param[out] primitives Primitives data of the resulting 3D object. - \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>. - \param isovalue Isovalue to extract from function. - \param x0 X-coordinate of the starting point. - \param y0 Y-coordinate of the starting point. - \param x1 X-coordinate of the ending point. - \param y1 Y-coordinate of the ending point. - \param size_x Resolution of the function along the X-axis. - \param size_y Resolution of the function along the Y-axis. - \note Use the marching squares algorithm for extracting the isolines. - **/ - template<typename tf, typename tfunc> - static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue, - const float x0, const float y0, const float x1, const float y1, - const int size_x=256, const int size_y=256) { - static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, - 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; - static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, - { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, - { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, - { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; - const unsigned int - _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), - _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), - nx = _nx?_nx:1, - ny = _ny?_ny:1, - nxm1 = nx - 1, - nym1 = ny - 1; - primitives.assign(); - if (!nxm1 || !nym1) return CImg<floatT>(); - const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1; - CImgList<floatT> vertices; - CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2); - CImg<floatT> values1(nx), values2(nx); - float X = x0, Y = y0, nX = X + dx, nY = Y + dy; - - // Fill first line with values - cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; } - - // Run the marching squares algorithm - for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) { - X = x0; nX = X + dx; - indices2.fill(-1); - for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) { - - // Determine square configuration - const float - val0 = values1(xi), - val1 = values1(nxi), - val2 = values2(nxi) = (float)func(nX,nY), - val3 = values2(xi) = (float)func(X,nY); - const unsigned int - configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) | - (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U), - edge = edges[configuration]; - - // Compute intersection vertices - if (edge) { - if ((edge&1) && indices1(xi,0)<0) { - const float Xi = X + (isovalue-val0)*dx/(val1-val0); - indices1(xi,0) = vertices.width(); - CImg<floatT>::vector(Xi,Y,0).move_to(vertices); - } - if ((edge&2) && indices1(nxi,1)<0) { - const float Yi = Y + (isovalue-val1)*dy/(val2-val1); - indices1(nxi,1) = vertices.width(); - CImg<floatT>::vector(nX,Yi,0).move_to(vertices); - } - if ((edge&4) && indices2(xi,0)<0) { - const float Xi = X + (isovalue-val3)*dx/(val2-val3); - indices2(xi,0) = vertices.width(); - CImg<floatT>::vector(Xi,nY,0).move_to(vertices); - } - if ((edge&8) && indices1(xi,1)<0) { - const float Yi = Y + (isovalue-val0)*dy/(val3-val0); - indices1(xi,1) = vertices.width(); - CImg<floatT>::vector(X,Yi,0).move_to(vertices); - } - - // Create segments - for (const int *segment = segments[configuration]; *segment!=-1; ) { - const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++); - const tf - i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)), - i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi)); - CImg<tf>::vector(i0,i1).move_to(primitives); - } - } - } - values1.swap(values2); - indices1.swap(indices2); - } - return vertices>'x'; - } - - //! Compute isolines of a function, as a 3D object \overloading. - template<typename tf> - static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue, - const float x0, const float y0, const float x1, const float y1, - const int size_x=256, const int size_y=256) { - const _functor2d_expr func(expression); - return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y); - } - - template<typename t> - static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2, - const unsigned int x, const unsigned int nx) { - switch (edge) { - case 0 : return (int)indices1(x,0); - case 1 : return (int)indices1(nx,1); - case 2 : return (int)indices2(x,0); - case 3 : return (int)indices1(x,1); - } - return 0; - } - - //! Compute isosurface of a function, as a 3D object. - /** - \param[out] primitives Primitives data of the resulting 3D object. - \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>. - \param isovalue Isovalue to extract. - \param x0 X-coordinate of the starting point. - \param y0 Y-coordinate of the starting point. - \param z0 Z-coordinate of the starting point. - \param x1 X-coordinate of the ending point. - \param y1 Y-coordinate of the ending point. - \param z1 Z-coordinate of the ending point. - \param size_x Resolution of the elevation function along the X-axis. - \param size_y Resolution of the elevation function along the Y-axis. - \param size_z Resolution of the elevation function along the Z-axis. - \note Use the marching cubes algorithm for extracting the isosurface. - **/ - template<typename tf, typename tfunc> - static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue, - const float x0, const float y0, const float z0, - const float x1, const float y1, const float z1, - const int size_x=32, const int size_y=32, const int size_z=32) { - static const unsigned int edges[256] = { - 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, - 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, - 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, - 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, - 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, - 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, - 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, - 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, - 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, - 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, - 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, - 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, - 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, - 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, - 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, - 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 - }; - - static const int triangles[256][16] = { - { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, - { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, - { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, - { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, - { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, - { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, - { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, - { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, - { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, - { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, - { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, - { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, - { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, - { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, - { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, - { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, - { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, - { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, - { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, - { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, - { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, - { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, - { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, - { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, - { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, - { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, - { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, - { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, - { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, - { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, - { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, - { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, - { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, - { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, - { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, - { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, - { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, - { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, - { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, - { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, - { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, - { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, - { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, - { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, - { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, - { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, - { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, - { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, - { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, - { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, - { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, - { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, - { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, - { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, - { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, - { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, - { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, - { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, - { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, - { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, - { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, - { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, - { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, - { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, - { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, - { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, - { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, - { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, - { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, - { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, - { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, - { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, - { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, - { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, - { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, - { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, - { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, - { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, - { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, - { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, - { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, - { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, - { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, - { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, - { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, - { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, - { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, - { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, - { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, - { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, - { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, - { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, - { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, - { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, - { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, - { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, - { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, - { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, - { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, - { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, - { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, - { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, - { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, - { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, - { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, - { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, - { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, - { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, - { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, - { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, - { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, - { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, - { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, - { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, - { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, - { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, - { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } - }; - - const unsigned int - _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), - _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), - _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)), - nx = _nx?_nx:1, - ny = _ny?_ny:1, - nz = _nz?_nz:1, - nxm1 = nx - 1, - nym1 = ny - 1, - nzm1 = nz - 1; - primitives.assign(); - if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>(); - const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1; - CImgList<floatT> vertices; - CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1); - CImg<floatT> values1(nx,ny), values2(nx,ny); - float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; - - // Fill the first plane with function values - Y = y0; - cimg_forY(values1,y) { - X = x0; - cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; } - Y+=dy; - } - - // Run Marching Cubes algorithm - Z = z0; nZ = Z + dz; - for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) { - Y = y0; nY = Y + dy; - indices2.fill(-1); - for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) { - X = x0; nX = X + dx; - for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) { - - // Determine cube configuration - const float - val0 = values1(xi,yi), - val1 = values1(nxi,yi), - val2 = values1(nxi,nyi), - val3 = values1(xi,nyi), - val4 = values2(xi,yi) = (float)func(X,Y,nZ), - val5 = values2(nxi,yi) = (float)func(nX,Y,nZ), - val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ), - val7 = values2(xi,nyi) = (float)func(X,nY,nZ); - - const unsigned int configuration = - (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) | (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U) | - (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U), - edge = edges[configuration]; - - // Compute intersection vertices - if (edge) { - if ((edge&1) && indices1(xi,yi,0)<0) { - const float Xi = X + (isovalue-val0)*dx/(val1-val0); - indices1(xi,yi,0) = vertices.width(); - CImg<floatT>::vector(Xi,Y,Z).move_to(vertices); - } - if ((edge&2) && indices1(nxi,yi,1)<0) { - const float Yi = Y + (isovalue-val1)*dy/(val2-val1); - indices1(nxi,yi,1) = vertices.width(); - CImg<floatT>::vector(nX,Yi,Z).move_to(vertices); - } - if ((edge&4) && indices1(xi,nyi,0)<0) { - const float Xi = X + (isovalue-val3)*dx/(val2-val3); - indices1(xi,nyi,0) = vertices.width(); - CImg<floatT>::vector(Xi,nY,Z).move_to(vertices); - } - if ((edge&8) && indices1(xi,yi,1)<0) { - const float Yi = Y + (isovalue-val0)*dy/(val3-val0); - indices1(xi,yi,1) = vertices.width(); - CImg<floatT>::vector(X,Yi,Z).move_to(vertices); - } - if ((edge&16) && indices2(xi,yi,0)<0) { - const float Xi = X + (isovalue-val4)*dx/(val5-val4); - indices2(xi,yi,0) = vertices.width(); - CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices); - } - if ((edge&32) && indices2(nxi,yi,1)<0) { - const float Yi = Y + (isovalue-val5)*dy/(val6-val5); - indices2(nxi,yi,1) = vertices.width(); - CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices); - } - if ((edge&64) && indices2(xi,nyi,0)<0) { - const float Xi = X + (isovalue-val7)*dx/(val6-val7); - indices2(xi,nyi,0) = vertices.width(); - CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices); - } - if ((edge&128) && indices2(xi,yi,1)<0) { - const float Yi = Y + (isovalue-val4)*dy/(val7-val4); - indices2(xi,yi,1) = vertices.width(); - CImg<floatT>::vector(X,Yi,nZ).move_to(vertices); - } - if ((edge&256) && indices1(xi,yi,2)<0) { - const float Zi = Z+ (isovalue-val0)*dz/(val4-val0); - indices1(xi,yi,2) = vertices.width(); - CImg<floatT>::vector(X,Y,Zi).move_to(vertices); - } - if ((edge&512) && indices1(nxi,yi,2)<0) { - const float Zi = Z + (isovalue-val1)*dz/(val5-val1); - indices1(nxi,yi,2) = vertices.width(); - CImg<floatT>::vector(nX,Y,Zi).move_to(vertices); - } - if ((edge&1024) && indices1(nxi,nyi,2)<0) { - const float Zi = Z + (isovalue-val2)*dz/(val6-val2); - indices1(nxi,nyi,2) = vertices.width(); - CImg<floatT>::vector(nX,nY,Zi).move_to(vertices); - } - if ((edge&2048) && indices1(xi,nyi,2)<0) { - const float Zi = Z + (isovalue-val3)*dz/(val7-val3); - indices1(xi,nyi,2) = vertices.width(); - CImg<floatT>::vector(X,nY,Zi).move_to(vertices); - } - - // Create triangles - for (const int *triangle = triangles[configuration]; *triangle!=-1; ) { - const unsigned int - p0 = (unsigned int)*(triangle++), - p1 = (unsigned int)*(triangle++), - p2 = (unsigned int)*(triangle++); - const tf - i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)), - i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)), - i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi)); - CImg<tf>::vector(i0,i2,i1).move_to(primitives); - } - } - } - } - cimg::swap(values1,values2); - cimg::swap(indices1,indices2); - } - return vertices>'x'; - } - - //! Compute isosurface of a function, as a 3D object \overloading. - template<typename tf> - static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue, - const float x0, const float y0, const float z0, - const float x1, const float y1, const float z1, - const int dx=32, const int dy=32, const int dz=32) { - const _functor3d_expr func(expression); - return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz); - } - - template<typename t> - static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2, - const unsigned int x, const unsigned int y, - const unsigned int nx, const unsigned int ny) { - switch (edge) { - case 0 : return indices1(x,y,0); - case 1 : return indices1(nx,y,1); - case 2 : return indices1(x,ny,0); - case 3 : return indices1(x,y,1); - case 4 : return indices2(x,y,0); - case 5 : return indices2(nx,y,1); - case 6 : return indices2(x,ny,0); - case 7 : return indices2(x,y,1); - case 8 : return indices1(x,y,2); - case 9 : return indices1(nx,y,2); - case 10 : return indices1(nx,ny,2); - case 11 : return indices1(x,ny,2); - } - return 0; - } - - // Define functors for accessing image values (used in previous functions). - struct _functor2d_int { - const CImg<T>& ref; - _functor2d_int(const CImg<T>& pref):ref(pref) {} - float operator()(const float x, const float y) const { - return (float)ref((int)x,(int)y); - } - }; - - struct _functor2d_float { - const CImg<T>& ref; - _functor2d_float(const CImg<T>& pref):ref(pref) {} - float operator()(const float x, const float y) const { - return (float)ref._linear_atXY(x,y); - } - }; - - struct _functor2d_expr { - _cimg_math_parser *mp; - ~_functor2d_expr() { mp->end(); delete mp; } - _functor2d_expr(const char *const expr):mp(0) { - mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0); - } - float operator()(const float x, const float y) const { - return (float)(*mp)(x,y,0,0); - } - }; - - struct _functor3d_int { - const CImg<T>& ref; - _functor3d_int(const CImg<T>& pref):ref(pref) {} - float operator()(const float x, const float y, const float z) const { - return (float)ref((int)x,(int)y,(int)z); - } - }; - - struct _functor3d_float { - const CImg<T>& ref; - _functor3d_float(const CImg<T>& pref):ref(pref) {} - float operator()(const float x, const float y, const float z) const { - return (float)ref._linear_atXYZ(x,y,z); - } - }; - - struct _functor3d_expr { - _cimg_math_parser *mp; - ~_functor3d_expr() { mp->end(); delete mp; } - _functor3d_expr(const char *const expr):mp(0) { - mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0); - } - float operator()(const float x, const float y, const float z) const { - return (float)(*mp)(x,y,z,0); - } - }; - - struct _functor4d_int { - const CImg<T>& ref; - _functor4d_int(const CImg<T>& pref):ref(pref) {} - float operator()(const float x, const float y, const float z, const unsigned int c) const { - return (float)ref((int)x,(int)y,(int)z,c); - } - }; - - //! Generate a 3D box object. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param size_x The width of the box (dimension along the X-axis). - \param size_y The height of the box (dimension along the Y-axis). - \param size_z The depth of the box (dimension along the Z-axis). - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - CImgList<unsigned int> faces3d; - const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30); - CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d); - \endcode - \image html ref_box3d.jpg - **/ - template<typename tf> - static CImg<floatT> box3d(CImgList<tf>& primitives, - const float size_x=200, const float size_y=100, const float size_z=100) { - primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5); - return CImg<floatT>(8,3,1,1, - 0.,size_x,size_x, 0., 0.,size_x,size_x, 0., - 0., 0.,size_y,size_y, 0., 0.,size_y,size_y, - 0., 0., 0., 0.,size_z,size_z,size_z,size_z); - } - - //! Generate a 3D cone. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param radius The radius of the cone basis. - \param size_z The cone's height. - \param subdivisions The number of basis angular subdivisions. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - CImgList<unsigned int> faces3d; - const CImg<float> points3d = CImg<float>::cone3d(faces3d,50); - CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d); - \endcode - \image html ref_cone3d.jpg - **/ - template<typename tf> - static CImg<floatT> cone3d(CImgList<tf>& primitives, - const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { - primitives.assign(); - if (!subdivisions) return CImg<floatT>(); - CImgList<floatT> vertices(2,1,3,1,1, - 0.,0.,size_z, - 0.,0.,0.); - for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { - const float a = (float)(angle*cimg::PI/180); - CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices); - } - const unsigned int nbr = vertices._width - 2; - for (unsigned int p = 0; p<nbr; ++p) { - const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr); - CImg<tf>::vector(1,next,curr).move_to(primitives); - CImg<tf>::vector(0,curr,next).move_to(primitives); - } - return vertices>'x'; - } - - //! Generate a 3D cylinder. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param radius The radius of the cylinder basis. - \param size_z The cylinder's height. - \param subdivisions The number of basis angular subdivisions. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - CImgList<unsigned int> faces3d; - const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50); - CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d); - \endcode - \image html ref_cylinder3d.jpg - **/ - template<typename tf> - static CImg<floatT> cylinder3d(CImgList<tf>& primitives, - const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { - primitives.assign(); - if (!subdivisions) return CImg<floatT>(); - CImgList<floatT> vertices(2,1,3,1,1, - 0.,0.,0., - 0.,0.,size_z); - for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { - const float a = (float)(angle*cimg::PI/180); - CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices); - CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices); - } - const unsigned int nbr = (vertices._width - 2)/2; - for (unsigned int p = 0; p<nbr; ++p) { - const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr)); - CImg<tf>::vector(0,next,curr).move_to(primitives); - CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives); - CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives); - } - return vertices>'x'; - } - - //! Generate a 3D torus. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param radius1 The large radius. - \param radius2 The small radius. - \param subdivisions1 The number of angular subdivisions for the large radius. - \param subdivisions2 The number of angular subdivisions for the small radius. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - CImgList<unsigned int> faces3d; - const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4); - CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d); - \endcode - \image html ref_torus3d.jpg - **/ - template<typename tf> - static CImg<floatT> torus3d(CImgList<tf>& primitives, - const float radius1=100, const float radius2=30, - const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) { - primitives.assign(); - if (!subdivisions1 || !subdivisions2) return CImg<floatT>(); - CImgList<floatT> vertices; - for (unsigned int v = 0; v<subdivisions1; ++v) { - const float - beta = (float)(v*2*cimg::PI/subdivisions1), - xc = radius1*(float)std::cos(beta), - yc = radius1*(float)std::sin(beta); - for (unsigned int u = 0; u<subdivisions2; ++u) { - const float - alpha = (float)(u*2*cimg::PI/subdivisions2), - x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)), - y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)), - z = radius2*(float)std::sin(alpha); - CImg<floatT>::vector(x,y,z).move_to(vertices); - } - } - for (unsigned int vv = 0; vv<subdivisions1; ++vv) { - const unsigned int nv = (vv + 1)%subdivisions1; - for (unsigned int uu = 0; uu<subdivisions2; ++uu) { - const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv; - CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives); - } - } - return vertices>'x'; - } - - //! Generate a 3D XY-plane. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param size_x The width of the plane (dimension along the X-axis). - \param size_y The height of the plane (dimensions along the Y-axis). - \param subdivisions_x The number of planar subdivisions along the X-axis. - \param subdivisions_y The number of planar subdivisions along the Y-axis. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - CImgList<unsigned int> faces3d; - const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50); - CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d); - \endcode - \image html ref_plane3d.jpg - **/ - template<typename tf> - static CImg<floatT> plane3d(CImgList<tf>& primitives, - const float size_x=100, const float size_y=100, - const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) { - primitives.assign(); - if (!subdivisions_x || !subdivisions_y) return CImg<floatT>(); - CImgList<floatT> vertices; - const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1; - const float fx = (float)size_x/w, fy = (float)size_y/h; - for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x) - CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices); - for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) { - const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w; - CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives); - } - return vertices>'x'; - } - - //! Generate a 3D sphere. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param radius The radius of the sphere (dimension along the X-axis). - \param subdivisions The number of recursive subdivisions from an initial icosahedron. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - CImgList<unsigned int> faces3d; - const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4); - CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d); - \endcode - \image html ref_sphere3d.jpg - **/ - template<typename tf> - static CImg<floatT> sphere3d(CImgList<tf>& primitives, - const float radius=50, const unsigned int subdivisions=3) { - - // Create initial icosahedron - primitives.assign(); - const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a; - CImgList<floatT> vertices(12,1,3,1,1, b,a,0., -b,a,0., -b,-a,0., b,-a,0., a,0.,b, a,0.,-b, - -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a); - primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6, - 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3, - 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2); - // edge - length/2 - float he = (float)a; - - // Recurse subdivisions - for (unsigned int i = 0; i<subdivisions; ++i) { - const unsigned int L = primitives._width; - he/=2; - const float he2 = he*he; - for (unsigned int l = 0; l<L; ++l) { - const unsigned int - p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2); - const float - x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2), - x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2), - x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2), - tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2, - nn0 = cimg::hypot(tnx0,tny0,tnz0), - tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2, - nn1 = cimg::hypot(tnx1,tny1,tnz1), - tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2, - nn2 = cimg::hypot(tnx2,tny2,tnz2), - nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0, - nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1, - nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2; - int i0 = -1, i1 = -1, i2 = -1; - cimglist_for(vertices,p) { - const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2); - if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p; - if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p; - if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p; - } - if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; } - if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; } - if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; } - primitives.remove(0); - CImg<tf>::vector(p0,i0,i1).move_to(primitives); - CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives); - CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives); - CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives); - } - } - return (vertices>'x')*=radius; - } - - //! Generate a 3D ellipsoid. - /** - \param[out] primitives The returned list of the 3D object primitives - (template type \e tf should be at least \e unsigned \e int). - \param tensor The tensor which gives the shape and size of the ellipsoid. - \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron. - \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1). - \par Example - \code - CImgList<unsigned int> faces3d; - const CImg<float> tensor = CImg<float>::diagonal(10,7,3), - points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4); - CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d); - \endcode - \image html ref_ellipsoid3d.jpg - **/ - template<typename tf, typename t> - static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives, - const CImg<t>& tensor, const unsigned int subdivisions=3) { - primitives.assign(); - if (!subdivisions) return CImg<floatT>(); - CImg<floatT> S, V; - tensor.symmetric_eigen(S,V); - const float orient = - (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) + - (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) + - (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2); - if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); } - const float l0 = S[0], l1 = S[1], l2 = S[2]; - CImg<floatT> vertices = sphere3d(primitives,1.,subdivisions); - vertices.get_shared_row(0)*=l0; - vertices.get_shared_row(1)*=l1; - vertices.get_shared_row(2)*=l2; - return V*vertices; - } - - //! Convert 3D object into a CImg3d representation. - /** - \param primitives Primitives data of the 3D object. - \param colors Colors data of the 3D object. - \param opacities Opacities data of the 3D object. - \param full_check Tells if full checking of the 3D object must be performed. - **/ - template<typename tp, typename tc, typename to> - CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives, - const CImgList<tc>& colors, - const to& opacities, - const bool full_check=true) { - return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this); - } - - //! Convert 3D object into a CImg3d representation \overloading. - template<typename tp, typename tc> - CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives, - const CImgList<tc>& colors, - const bool full_check=true) { - return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this); - } - - //! Convert 3D object into a CImg3d representation \overloading. - template<typename tp> - CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives, - const bool full_check=true) { - return get_object3dtoCImg3d(primitives,full_check).move_to(*this); - } - - //! Convert 3D object into a CImg3d representation \overloading. - CImg<T>& object3dtoCImg3d(const bool full_check=true) { - return get_object3dtoCImg3d(full_check).move_to(*this); - } - - //! Convert 3D object into a CImg3d representation \newinstance. - template<typename tp, typename tc, typename to> - CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives, - const CImgList<tc>& colors, - const to& opacities, - const bool full_check=true) const { - CImg<charT> error_message(1024); - if (!is_object3d(primitives,colors,opacities,full_check,error_message)) - throw CImgInstanceException(_cimg_instance - "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).", - cimg_instance,_width,primitives._width,error_message.data()); - CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities)); - float *ptrd = res._data; - - // Put magick number. - *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f; - *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f; - - // Put number of vertices and primitives. - *(ptrd++) = cimg::uint2float(_width); - *(ptrd++) = cimg::uint2float(primitives._width); - - // Put vertex data. - if (is_empty() || !primitives) return res; - const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2); - cimg_forX(*this,p) { - *(ptrd++) = (float)*(ptrx++); - *(ptrd++) = (float)*(ptry++); - *(ptrd++) = (float)*(ptrz++); - } - - // Put primitive data. - cimglist_for(primitives,p) { - *(ptrd++) = (float)primitives[p].size(); - const tp *ptrp = primitives[p]._data; - cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++)); - } - - // Put color/texture data. - const unsigned int csiz = std::min(colors._width,primitives._width); - for (int c = 0; c<(int)csiz; ++c) { - const CImg<tc>& color = colors[c]; - const tc *ptrc = color._data; - if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; } - else { - *(ptrd++) = -128.f; - int shared_ind = -1; - if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; } - if (shared_ind<0) { - *(ptrd++) = (float)color._width; - *(ptrd++) = (float)color._height; - *(ptrd++) = (float)color._spectrum; - cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++); - } else { - *(ptrd++) = (float)shared_ind; - *(ptrd++) = 0; - *(ptrd++) = 0; - } - } - } - const int csiz2 = primitives.width() - colors.width(); - for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.f; *(ptrd++) = 200.f; *(ptrd++) = 200.f; } - - // Put opacity data. - ptrd = _object3dtoCImg3d(opacities,ptrd); - const float *ptre = res.end(); - while (ptrd<ptre) *(ptrd++) = 1.f; - return res; - } - - template<typename to> - float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const { - cimglist_for(opacities,o) { - const CImg<to>& opacity = opacities[o]; - const to *ptro = opacity._data; - if (opacity.size()==1) *(ptrd++) = (float)*ptro; - else { - *(ptrd++) = -128.f; - int shared_ind = -1; - if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; } - if (shared_ind<0) { - *(ptrd++) = (float)opacity._width; - *(ptrd++) = (float)opacity._height; - *(ptrd++) = (float)opacity._spectrum; - cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++); - } else { - *(ptrd++) = (float)shared_ind; - *(ptrd++) = 0; - *(ptrd++) = 0; - } - } - } - return ptrd; - } - - template<typename to> - float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const { - const to *ptro = opacities._data; - cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++); - return ptrd; - } - - template<typename tp, typename tc, typename to> - unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives, - const CImgList<tc>& colors, - const CImgList<to>& opacities) const { - unsigned int siz = 8U + 3*_width; - cimglist_for(primitives,p) siz+=primitives[p].size() + 1; - for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { - if (colors[c].is_shared()) siz+=4; - else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; } - } - if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width); - cimglist_for(opacities,o) { - if (opacities[o].is_shared()) siz+=4; - else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; } - } - siz+=primitives._width - opacities._width; - return siz; - } - - template<typename tp, typename tc, typename to> - unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives, - const CImgList<tc>& colors, - const CImg<to>& opacities) const { - unsigned int siz = 8U + 3*_width; - cimglist_for(primitives,p) siz+=primitives[p].size() + 1; - for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { - const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; - } - if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width); - siz+=primitives.size(); - cimg::unused(opacities); - return siz; - } - - //! Convert 3D object into a CImg3d representation \overloading. - template<typename tp, typename tc> - CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives, - const CImgList<tc>& colors, - const bool full_check=true) const { - CImgList<T> opacities; - return get_object3dtoCImg3d(primitives,colors,opacities,full_check); - } - - //! Convert 3D object into a CImg3d representation \overloading. - template<typename tp> - CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives, - const bool full_check=true) const { - CImgList<T> colors, opacities; - return get_object3dtoCImg3d(primitives,colors,opacities,full_check); - } - - //! Convert 3D object into a CImg3d representation \overloading. - CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const { - CImgList<T> opacities, colors; - CImgList<uintT> primitives(width(),1,1,1,1); - cimglist_for(primitives,p) primitives(p,0) = p; - return get_object3dtoCImg3d(primitives,colors,opacities,full_check); - } - - //! Convert CImg3d representation into a 3D object. - /** - \param[out] primitives Primitives data of the 3D object. - \param[out] colors Colors data of the 3D object. - \param[out] opacities Opacities data of the 3D object. - \param full_check Tells if full checking of the 3D object must be performed. - **/ - template<typename tp, typename tc, typename to> - CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, - CImgList<tc>& colors, - CImgList<to>& opacities, - const bool full_check=true) { - return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this); - } - - //! Convert CImg3d representation into a 3D object \newinstance. - template<typename tp, typename tc, typename to> - CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives, - CImgList<tc>& colors, - CImgList<to>& opacities, - const bool full_check=true) const { - CImg<charT> error_message(1024); - if (!is_CImg3d(full_check,error_message)) - throw CImgInstanceException(_cimg_instance - "CImg3dtoobject3d(): image instance is not a CImg3d (%s).", - cimg_instance,error_message.data()); - const T *ptrs = _data + 6; - const unsigned int - nb_points = cimg::float2uint((float)*(ptrs++)), - nb_primitives = cimg::float2uint((float)*(ptrs++)); - const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose(); - ptrs+=3*nb_points; - primitives.assign(nb_primitives); - cimglist_for(primitives,p) { - const unsigned int nb_inds = (unsigned int)*(ptrs++); - primitives[p].assign(1,nb_inds); - tp *ptrp = primitives[p]._data; - for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++)); - } - colors.assign(nb_primitives); - cimglist_for(colors,c) { - if (*ptrs==(T)-128) { - ++ptrs; - const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++); - if (!h && !s) colors[c].assign(colors[w],true); - else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; } - } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; } - } - opacities.assign(nb_primitives); - cimglist_for(opacities,o) { - if (*ptrs==(T)-128) { - ++ptrs; - const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++); - if (!h && !s) opacities[o].assign(opacities[w],true); - else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; } - } else opacities[o].assign(1,1,1,1,*(ptrs++)); - } - return points; - } - - //@} - //--------------------------- - // - //! \name Drawing Functions - //@{ - //--------------------------- - -#define cimg_init_scanline(color,opacity) \ - const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \ - const ulongT _sc_whd = (ulongT)_width*_height*_depth - -#define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \ - _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd) - - // [internal] The following _draw_scanline() routines are *non user-friendly functions*, - // used only for internal purpose. - // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid. - template<typename tc> - CImg<T>& _draw_scanline(const int x0, const int x1, const int y, - const tc *const color, const float opacity, - const float brightness, - const float nopacity, const float copacity, const ulongT whd) { - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0; - if (dx>=0) { - const tc *col = color; - const ulongT off = whd - dx - 1; - T *ptrd = data(nx0,y); - if (opacity>=1) { // ** Opaque drawing ** - if (brightness==1) { // Brightness==1 - if (sizeof(T)!=1) cimg_forC(*this,c) { - const T val = (T)*(col++); - for (int x = dx; x>=0; --x) *(ptrd++) = val; - ptrd+=off; - } else cimg_forC(*this,c) { - const T val = (T)*(col++); - std::memset(ptrd,(int)val,dx + 1); - ptrd+=whd; - } - } else if (brightness<1) { // Brightness<1 - if (sizeof(T)!=1) cimg_forC(*this,c) { - const T val = (T)(*(col++)*brightness); - for (int x = dx; x>=0; --x) *(ptrd++) = val; - ptrd+=off; - } else cimg_forC(*this,c) { - const T val = (T)(*(col++)*brightness); - std::memset(ptrd,(int)val,dx + 1); - ptrd+=whd; - } - } else { // Brightness>1 - if (sizeof(T)!=1) cimg_forC(*this,c) { - const T val = (T)((2-brightness)**(col++) + (brightness - 1)*maxval); - for (int x = dx; x>=0; --x) *(ptrd++) = val; - ptrd+=off; - } else cimg_forC(*this,c) { - const T val = (T)((2-brightness)**(col++) + (brightness - 1)*maxval); - std::memset(ptrd,(int)val,dx + 1); - ptrd+=whd; - } - } - } else { // ** Transparent drawing ** - if (brightness==1) { // Brightness==1 - cimg_forC(*this,c) { - const Tfloat val = *(col++)*nopacity; - for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } - ptrd+=off; - } - } else if (brightness<=1) { // Brightness<1 - cimg_forC(*this,c) { - const Tfloat val = *(col++)*brightness*nopacity; - for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } - ptrd+=off; - } - } else { // Brightness>1 - cimg_forC(*this,c) { - const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*maxval)*nopacity; - for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } - ptrd+=off; - } - } - } - } - return *this; - } - - //! Draw a 3D point. - /** - \param x0 X-coordinate of the point. - \param y0 Y-coordinate of the point. - \param z0 Z-coordinate of the point. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \note - - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. - \par Example: - \code - CImg<unsigned char> img(100,100,1,3,0); - const unsigned char color[] = { 255,128,64 }; - img.draw_point(50,50,color); - \endcode - **/ - template<typename tc> - CImg<T>& draw_point(const int x0, const int y0, const int z0, - const tc *const color, const float opacity=1) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_point(): Specified color is (null).", - cimg_instance); - if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) { - const ulongT whd = (ulongT)_width*_height*_depth; - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - T *ptrd = data(x0,y0,z0,0); - const tc *col = color; - if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } - else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } - } - return *this; - } - - //! Draw a 2D point \simplification. - template<typename tc> - CImg<T>& draw_point(const int x0, const int y0, - const tc *const color, const float opacity=1) { - return draw_point(x0,y0,0,color,opacity); - } - - // Draw a points cloud. - /** - \param points Image of vertices coordinates. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename t, typename tc> - CImg<T>& draw_point(const CImg<t>& points, - const tc *const color, const float opacity=1) { - if (is_empty() || !points) return *this; - switch (points._height) { - case 0 : case 1 : - throw CImgArgumentException(_cimg_instance - "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).", - cimg_instance, - points._width,points._height,points._depth,points._spectrum,points._data); - case 2 : { - cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity); - } break; - default : { - cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity); - } - } - return *this; - } - - //! Draw a 2D line. - /** - \param x0 X-coordinate of the starting line point. - \param y0 Y-coordinate of the starting line point. - \param x1 X-coordinate of the ending line point. - \param y1 Y-coordinate of the ending line point. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch Tells if a reinitialization of the hash state must be done. - \note - - Line routine uses Bresenham's algorithm. - - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. - \par Example: - \code - CImg<unsigned char> img(100,100,1,3,0); - const unsigned char color[] = { 255,128,64 }; - img.draw_line(40,40,80,70,color); - \endcode - **/ - template<typename tc> - CImg<T>& draw_line(const int x0, const int y0, - const int x1, const int y1, - const tc *const color, const float opacity=1, - const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_line(): Specified color is (null).", - cimg_instance); - static unsigned int hatch = ~0U - (~0U>>1); - if (init_hatch) hatch = ~0U - (~0U>>1); - const bool xdir = x0<x1, ydir = y0<y1; - int - nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1, - &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, - &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0, - &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, - &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0; - if (xright<0 || xleft>=width()) return *this; - if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; } - if (xright>=width()) { - yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft)); - xright = width() - 1; - } - if (ydown<0 || yup>=height()) return *this; - if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; } - if (ydown>=height()) { - xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup)); - ydown = height() - 1; - } - T *ptrd0 = data(nx0,ny0); - int dx = xright - xleft, dy = ydown - yup; - const bool steep = dy>dx; - if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const longT - offx = (longT)(nx0<nx1?1:-1)*(steep?width():1), - offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()); - const ulongT wh = (ulongT)_width*_height; - if (opacity>=1) { - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - if (pattern&hatch) { - T *ptrd = ptrd0; const tc* col = color; - cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; } - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } - } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - if (pattern&hatch) { - T *ptrd = ptrd0; const tc* col = color; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - T *ptrd = ptrd0; const tc* col = color; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } - } - return *this; - } - - //! Draw a 2D line, with z-buffering. - /** - \param zbuffer Zbuffer image. - \param x0 X-coordinate of the starting point. - \param y0 Y-coordinate of the starting point. - \param z0 Z-coordinate of the starting point - \param x1 X-coordinate of the ending point. - \param y1 Y-coordinate of the ending point. - \param z1 Z-coordinate of the ending point. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch Tells if a reinitialization of the hash state must be done. - **/ - template<typename tz,typename tc> - CImg<T>& draw_line(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const tc *const color, const float opacity=1, - const unsigned int pattern=~0U, const bool init_hatch=true) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_line(): Specified color is (null).", - cimg_instance); - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - static unsigned int hatch = ~0U - (~0U>>1); - if (init_hatch) hatch = ~0U - (~0U>>1); - const bool xdir = x0<x1, ydir = y0<y1; - int - nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1, - &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, - &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0, - &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, - &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0; - tzfloat - Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0, - &zleft = xdir?nz0:nz1, - &zright = xdir?nz1:nz0, - &zup = ydir?nz0:nz1, - &zdown = ydir?nz1:nz0; - if (xright<0 || xleft>=width()) return *this; - if (xleft<0) { - const float D = (float)xright - xleft; - yleft-=(int)((float)xleft*((float)yright - yleft)/D); - zleft-=(tzfloat)xleft*(zright - zleft)/D; - xleft = 0; - } - if (xright>=width()) { - const float d = (float)xright - width(), D = (float)xright - xleft; - yright-=(int)(d*((float)yright - yleft)/D); - zright-=(tzfloat)d*(zright - zleft)/D; - xright = width() - 1; - } - if (ydown<0 || yup>=height()) return *this; - if (yup<0) { - const float D = (float)ydown - yup; - xup-=(int)((float)yup*((float)xdown - xup)/D); - zup-=(tzfloat)yup*(zdown - zup)/D; - yup = 0; - } - if (ydown>=height()) { - const float d = (float)ydown - height(), D = (float)ydown - yup; - xdown-=(int)(d*((float)xdown - xup)/D); - zdown-=(tzfloat)d*(zdown - zup)/D; - ydown = height() - 1; - } - T *ptrd0 = data(nx0,ny0); - tz *ptrz = zbuffer.data(nx0,ny0); - int dx = xright - xleft, dy = ydown - yup; - const bool steep = dy>dx; - if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const longT - offx = (longT)(nx0<nx1?1:-1)*(steep?width():1), - offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()); - const ulongT - wh = (ulongT)_width*_height, - ndx = (ulongT)(dx>0?dx:1); - if (opacity>=1) { - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz && pattern&hatch) { - *ptrz = (tz)z; - T *ptrd = ptrd0; const tc *col = color; - cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz) { - *ptrz = (tz)z; - T *ptrd = ptrd0; const tc *col = color; - cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; } - } - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } - } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz && pattern&hatch) { - *ptrz = (tz)z; - T *ptrd = ptrd0; const tc *col = color; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz) { - *ptrz = (tz)z; - T *ptrd = ptrd0; const tc *col = color; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } - } - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } - } - return *this; - } - - //! Draw a 3D line. - /** - \param x0 X-coordinate of the starting point. - \param y0 Y-coordinate of the starting point. - \param z0 Z-coordinate of the starting point - \param x1 X-coordinate of the ending point. - \param y1 Y-coordinate of the ending point. - \param z1 Z-coordinate of the ending point. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch Tells if a reinitialization of the hash state must be done. - **/ - template<typename tc> - CImg<T>& draw_line(const int x0, const int y0, const int z0, - const int x1, const int y1, const int z1, - const tc *const color, const float opacity=1, - const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_line(): Specified color is (null).", - cimg_instance); - static unsigned int hatch = ~0U - (~0U>>1); - if (init_hatch) hatch = ~0U - (~0U>>1); - int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1; - if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); - if (nx1<0 || nx0>=width()) return *this; - if (nx0<0) { - const float D = 1.f + nx1 - nx0; - ny0-=(int)((float)nx0*(1.f + ny1 - ny0)/D); - nz0-=(int)((float)nx0*(1.f + nz1 - nz0)/D); - nx0 = 0; - } - if (nx1>=width()) { - const float d = (float)nx1 - width(), D = 1.f + nx1 - nx0; - ny1+=(int)(d*(1.f + ny0 - ny1)/D); - nz1+=(int)(d*(1.f + nz0 - nz1)/D); - nx1 = width() - 1; - } - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); - if (ny1<0 || ny0>=height()) return *this; - if (ny0<0) { - const float D = 1.f + ny1 - ny0; - nx0-=(int)((float)ny0*(1.f + nx1 - nx0)/D); - nz0-=(int)((float)ny0*(1.f + nz1 - nz0)/D); - ny0 = 0; - } - if (ny1>=height()) { - const float d = (float)ny1 - height(), D = 1.f + ny1 - ny0; - nx1+=(int)(d*(1.f + nx0 - nx1)/D); - nz1+=(int)(d*(1.f + nz0 - nz1)/D); - ny1 = height() - 1; - } - if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); - if (nz1<0 || nz0>=depth()) return *this; - if (nz0<0) { - const float D = 1.f + nz1 - nz0; - nx0-=(int)((float)nz0*(1.f + nx1 - nx0)/D); - ny0-=(int)((float)nz0*(1.f + ny1 - ny0)/D); - nz0 = 0; - } - if (nz1>=depth()) { - const float d = (float)nz1 - depth(), D = 1.f + nz1 - nz0; - nx1+=(int)(d*(1.f + nx0 - nx1)/D); - ny1+=(int)(d*(1.f + ny0 - ny1)/D); - nz1 = depth() - 1; - } - const unsigned int dmax = (unsigned int)cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0); - const ulongT whd = (ulongT)_width*_height*_depth; - const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax; - float x = (float)nx0, y = (float)ny0, z = (float)nz0; - if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) { - if (!(~pattern) || (~pattern && pattern&hatch)) { - T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z); - const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } - } - x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); } - } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - for (unsigned int t = 0; t<=dmax; ++t) { - if (!(~pattern) || (~pattern && pattern&hatch)) { - T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z); - const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } - } - x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); } - } - } - return *this; - } - - //! Draw a textured 2D line. - /** - \param x0 X-coordinate of the starting line point. - \param y0 Y-coordinate of the starting line point. - \param x1 X-coordinate of the ending line point. - \param y1 Y-coordinate of the ending line point. - \param texture Texture image defining the pixel colors. - \param tx0 X-coordinate of the starting texture point. - \param ty0 Y-coordinate of the starting texture point. - \param tx1 X-coordinate of the ending texture point. - \param ty1 Y-coordinate of the ending texture point. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch Tells if the hash variable must be reinitialized. - \note - - Line routine uses the well known Bresenham's algorithm. - \par Example: - \code - CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm"); - const unsigned char color[] = { 255,128,64 }; - img.draw_line(40,40,80,70,texture,0,0,255,255); - \endcode - **/ - template<typename tc> - CImg<T>& draw_line(const int x0, const int y0, - const int x1, const int y1, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const float opacity=1, - const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty()) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); - static unsigned int hatch = ~0U - (~0U>>1); - if (init_hatch) hatch = ~0U - (~0U>>1); - const bool xdir = x0<x1, ydir = y0<y1; - int - dtx = tx1-tx0, dty = ty1-ty0, - nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1, - tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1, - &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0, - &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0, - &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0, - &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0; - if (xright<0 || xleft>=width()) return *this; - if (xleft<0) { - const float D = (float)xright - xleft; - yleft-=(int)((float)xleft*((float)yright - yleft)/D); - txleft-=(int)((float)xleft*((float)txright - txleft)/D); - tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D); - xleft = 0; - } - if (xright>=width()) { - const float d = (float)xright - width(), D = (float)xright - xleft; - yright-=(int)(d*((float)yright - yleft)/D); - txright-=(int)(d*((float)txright - txleft)/D); - tyright-=(int)(d*((float)tyright - tyleft)/D); - xright = width() - 1; - } - if (ydown<0 || yup>=height()) return *this; - if (yup<0) { - const float D = (float)ydown - yup; - xup-=(int)((float)yup*((float)xdown - xup)/D); - txup-=(int)((float)yup*((float)txdown - txup)/D); - tyup-=(int)((float)yup*((float)tydown - tyup)/D); - yup = 0; - } - if (ydown>=height()) { - const float d = (float)ydown - height(), D = (float)ydown - yup; - xdown-=(int)(d*((float)xdown - xup)/D); - txdown-=(int)(d*((float)txdown - txup)/D); - tydown-=(int)(d*((float)tydown - tyup)/D); - ydown = height() - 1; - } - T *ptrd0 = data(nx0,ny0); - int dx = xright - xleft, dy = ydown - yup; - const bool steep = dy>dx; - if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const longT - offx = (longT)(nx0<nx1?1:-1)*(steep?width():1), - offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()), - ndx = (longT)(dx>0?dx:1); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height; - - if (opacity>=1) { - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - if (pattern&hatch) { - T *ptrd = ptrd0; - const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - T *ptrd = ptrd0; - const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } - } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - T *ptrd = ptrd0; - if (pattern&hatch) { - const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - T *ptrd = ptrd0; - const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; - const tc *col = &texture._atXY(tx,ty); - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } - } - return *this; - } - - //! Draw a textured 2D line, with perspective correction. - /** - \param x0 X-coordinate of the starting point. - \param y0 Y-coordinate of the starting point. - \param z0 Z-coordinate of the starting point - \param x1 X-coordinate of the ending point. - \param y1 Y-coordinate of the ending point. - \param z1 Z-coordinate of the ending point. - \param texture Texture image defining the pixel colors. - \param tx0 X-coordinate of the starting texture point. - \param ty0 Y-coordinate of the starting texture point. - \param tx1 X-coordinate of the ending texture point. - \param ty1 Y-coordinate of the ending texture point. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch Tells if the hash variable must be reinitialized. - **/ - template<typename tc> - CImg<T>& draw_line(const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const float opacity=1, - const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty() && z0<=0 && z1<=0) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) - return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); - static unsigned int hatch = ~0U - (~0U>>1); - if (init_hatch) hatch = ~0U - (~0U>>1); - const bool xdir = x0<x1, ydir = y0<y1; - int - nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1, - &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, - &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0, - &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, - &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0; - float - Tx0 = tx0/z0, Tx1 = tx1/z1, - Ty0 = ty0/z0, Ty1 = ty1/z1, - Z0 = 1/z0, Z1 = 1/z1, - dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0, - tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1, - &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, - &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0, - &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, - &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0; - if (xright<0 || xleft>=width()) return *this; - if (xleft<0) { - const float D = (float)xright - xleft; - yleft-=(int)((float)xleft*((float)yright - yleft)/D); - zleft-=(float)xleft*(zright - zleft)/D; - txleft-=(float)xleft*(txright - txleft)/D; - tyleft-=(float)xleft*(tyright - tyleft)/D; - xleft = 0; - } - if (xright>=width()) { - const float d = (float)xright - width(), D = (float)xright - xleft; - yright-=(int)(d*((float)yright - yleft)/D); - zright-=d*(zright - zleft)/D; - txright-=d*(txright - txleft)/D; - tyright-=d*(tyright - tyleft)/D; - xright = width() - 1; - } - if (ydown<0 || yup>=height()) return *this; - if (yup<0) { - const float D = (float)ydown - yup; - xup-=(int)((float)yup*((float)xdown - xup)/D); - zup-=(float)yup*(zdown - zup)/D; - txup-=(float)yup*(txdown - txup)/D; - tyup-=(float)yup*(tydown - tyup)/D; - yup = 0; - } - if (ydown>=height()) { - const float d = (float)ydown - height(), D = (float)ydown - yup; - xdown-=(int)(d*((float)xdown - xup)/D); - zdown-=d*(zdown - zup)/D; - txdown-=d*(txdown - txup)/D; - tydown-=d*(tydown - tyup)/D; - ydown = height() - 1; - } - T *ptrd0 = data(nx0,ny0); - int dx = xright - xleft, dy = ydown - yup; - const bool steep = dy>dx; - if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const longT - offx = (longT)(nx0<nx1?1:-1)*(steep?width():1), - offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()), - ndx = (longT)(dx>0?dx:1); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height; - - if (opacity>=1) { - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - if (pattern&hatch) { - const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } - } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - if (pattern&hatch) { - const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - ptrd0+=offx; - if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } - } - } - return *this; - } - - //! Draw a textured 2D line, with perspective correction and z-buffering. - /** - \param zbuffer Z-buffer image. - \param x0 X-coordinate of the starting point. - \param y0 Y-coordinate of the starting point. - \param z0 Z-coordinate of the starting point - \param x1 X-coordinate of the ending point. - \param y1 Y-coordinate of the ending point. - \param z1 Z-coordinate of the ending point. - \param texture Texture image defining the pixel colors. - \param tx0 X-coordinate of the starting texture point. - \param ty0 Y-coordinate of the starting texture point. - \param tx1 X-coordinate of the ending texture point. - \param ty1 Y-coordinate of the ending texture point. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch Tells if the hash variable must be reinitialized. - **/ - template<typename tz, typename tc> - CImg<T>& draw_line(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const float opacity=1, - const unsigned int pattern=~0U, const bool init_hatch=true) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0) return *this; - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) - return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); - static unsigned int hatch = ~0U - (~0U>>1); - if (init_hatch) hatch = ~0U - (~0U>>1); - const bool xdir = x0<x1, ydir = y0<y1; - int - nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1, - &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, - &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0, - &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, - &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0; - float - Tx0 = tx0/z0, Tx1 = tx1/z1, - Ty0 = ty0/z0, Ty1 = ty1/z1, - dtx = Tx1 - Tx0, dty = Ty1 - Ty0, - tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, - &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, - &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0, - &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, - &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0; - tzfloat - Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, - dz = Z1 - Z0, nz0 = Z0, nz1 = Z1, - &zleft = xdir?nz0:nz1, - &zright = xdir?nz1:nz0, - &zup = ydir?nz0:nz1, - &zdown = ydir?nz1:nz0; - if (xright<0 || xleft>=width()) return *this; - if (xleft<0) { - const float D = (float)xright - xleft; - yleft-=(int)((float)xleft*((float)yright - yleft)/D); - zleft-=(float)xleft*(zright - zleft)/D; - txleft-=(float)xleft*(txright - txleft)/D; - tyleft-=(float)xleft*(tyright - tyleft)/D; - xleft = 0; - } - if (xright>=width()) { - const float d = (float)xright - width(), D = (float)xright - xleft; - yright-=(int)(d*((float)yright - yleft)/D); - zright-=d*(zright - zleft)/D; - txright-=d*(txright - txleft)/D; - tyright-=d*(tyright - tyleft)/D; - xright = width() - 1; - } - if (ydown<0 || yup>=height()) return *this; - if (yup<0) { - const float D = (float)ydown - yup; - xup-=(int)((float)yup*((float)xdown - xup)/D); - zup-=yup*(zdown - zup)/D; - txup-=yup*(txdown - txup)/D; - tyup-=yup*(tydown - tyup)/D; - yup = 0; - } - if (ydown>=height()) { - const float d = (float)ydown - height(), D = (float)ydown - yup; - xdown-=(int)(d*((float)xdown - xup)/D); - zdown-=d*(zdown - zup)/D; - txdown-=d*(txdown - txup)/D; - tydown-=d*(tydown - tyup)/D; - ydown = height() - 1; - } - T *ptrd0 = data(nx0,ny0); - tz *ptrz = zbuffer.data(nx0,ny0); - int dx = xright - xleft, dy = ydown - yup; - const bool steep = dy>dx; - if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const longT - offx = (longT)(nx0<nx1?1:-1)*(steep?width():1), - offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()), - ndx = (longT)(dx>0?dx:1); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height; - - if (opacity>=1) { - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - if (pattern&hatch) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz) { - *ptrz = (tz)z; - const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz) { - *ptrz = (tz)z; - const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; } - } - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } - } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { - if (pattern&hatch) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz) { - *ptrz = (tz)z; - const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - } - hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } else for (int error = dx>>1, x = 0; x<=dx; ++x) { - const tzfloat z = Z0 + x*dz/ndx; - if (z>=(tzfloat)*ptrz) { - *ptrz = (tz)z; - const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z)); - T *ptrd = ptrd0; - cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } - } - ptrd0+=offx; ptrz+=offx; - if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } - } - } - return *this; - } - - //! Draw a set of consecutive lines. - /** - \param points Coordinates of vertices, stored as a list of vectors. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch If set to true, init hatch motif. - \note - - This function uses several call to the single CImg::draw_line() procedure, - depending on the vectors size in \p points. - **/ - template<typename t, typename tc> - CImg<T>& draw_line(const CImg<t>& points, - const tc *const color, const float opacity=1, - const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty() || !points || points._width<2) return *this; - bool ninit_hatch = init_hatch; - switch (points._height) { - case 0 : case 1 : - throw CImgArgumentException(_cimg_instance - "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", - cimg_instance, - points._width,points._height,points._depth,points._spectrum,points._data); - - case 2 : { - const int x0 = (int)points(0,0), y0 = (int)points(0,1); - int ox = x0, oy = y0; - for (unsigned int i = 1; i<points._width; ++i) { - const int x = (int)points(i,0), y = (int)points(i,1); - draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = x; oy = y; - } - } break; - default : { - const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2); - int ox = x0, oy = y0, oz = z0; - for (unsigned int i = 1; i<points._width; ++i) { - const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2); - draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = x; oy = y; oz = z; - } - } - } - return *this; - } - - //! Draw a 2D arrow. - /** - \param x0 X-coordinate of the starting arrow point (tail). - \param y0 Y-coordinate of the starting arrow point (tail). - \param x1 X-coordinate of the ending arrow point (head). - \param y1 Y-coordinate of the ending arrow point (head). - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param angle Aperture angle of the arrow head. - \param length Length of the arrow head. If negative, describes a percentage of the arrow length. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - **/ - template<typename tc> - CImg<T>& draw_arrow(const int x0, const int y0, - const int x1, const int y1, - const tc *const color, const float opacity=1, - const float angle=30, const float length=-10, - const unsigned int pattern=~0U) { - if (is_empty()) return *this; - const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v, - deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f, - l = (length>=0)?length:-length*(float)std::sqrt(sq)/100; - if (sq>0) { - const float - cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg), - cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg); - const int - xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl), - xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr), - xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2; - draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); - } else draw_point(x0,y0,color,opacity); - return *this; - } - - //! Draw a 2D spline. - /** - \param x0 X-coordinate of the starting curve point - \param y0 Y-coordinate of the starting curve point - \param u0 X-coordinate of the starting velocity - \param v0 Y-coordinate of the starting velocity - \param x1 X-coordinate of the ending curve point - \param y1 Y-coordinate of the ending curve point - \param u1 X-coordinate of the ending velocity - \param v1 Y-coordinate of the ending velocity - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param precision Curve drawing precision. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch If \c true, init hatch motif. - \note - - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points - and corresponding velocity vectors. - - The spline is drawn as a serie of connected segments. The \p precision parameter sets the - average number of pixels in each drawn segment. - - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), - (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point - and (\p xa,\p ya), (\p xb,\p yb) are two - \e control points. - The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from - the control points as - \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). - \par Example: - \code - CImg<unsigned char> img(100,100,1,3,0); - const unsigned char color[] = { 255,255,255 }; - img.draw_spline(30,30,0,100,90,40,0,-100,color); - \endcode - **/ - template<typename tc> - CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0, - const int x1, const int y1, const float u1, const float v1, - const tc *const color, const float opacity=1, - const float precision=0.25, const unsigned int pattern=~0U, - const bool init_hatch=true) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_spline(): Specified color is (null).", - cimg_instance); - if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity); - bool ninit_hatch = init_hatch; - const float - ax = u0 + u1 + 2*(x0 - x1), - bx = 3*(x1 - x0) - 2*u0 - u1, - ay = v0 + v1 + 2*(y0 - y1), - by = 3*(y1 - y0) - 2*v0 - v1, - _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); - int ox = x0, oy = y0; - for (float t = 0; t<1; t+=_precision) { - const float t2 = t*t, t3 = t2*t; - const int - nx = (int)(ax*t3 + bx*t2 + u0*t + x0), - ny = (int)(ay*t3 + by*t2 + v0*t + y0); - draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = nx; oy = ny; - } - return draw_line(ox,oy,x1,y1,color,opacity,pattern,false); - } - - //! Draw a 3D spline \overloading. - /** - \note - - Similar to CImg::draw_spline() for a 3D spline in a volumetric image. - **/ - template<typename tc> - CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0, - const int x1, const int y1, const int z1, const float u1, const float v1, const float w1, - const tc *const color, const float opacity=1, - const float precision=4, const unsigned int pattern=~0U, - const bool init_hatch=true) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_spline(): Specified color is (null).", - cimg_instance); - if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity); - bool ninit_hatch = init_hatch; - const float - ax = u0 + u1 + 2*(x0 - x1), - bx = 3*(x1 - x0) - 2*u0 - u1, - ay = v0 + v1 + 2*(y0 - y1), - by = 3*(y1 - y0) - 2*v0 - v1, - az = w0 + w1 + 2*(z0 - z1), - bz = 3*(z1 - z0) - 2*w0 - w1, - _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); - int ox = x0, oy = y0, oz = z0; - for (float t = 0; t<1; t+=_precision) { - const float t2 = t*t, t3 = t2*t; - const int - nx = (int)(ax*t3 + bx*t2 + u0*t + x0), - ny = (int)(ay*t3 + by*t2 + v0*t + y0), - nz = (int)(az*t3 + bz*t2 + w0*t + z0); - draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = nx; oy = ny; oz = nz; - } - return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false); - } - - //! Draw a textured 2D spline. - /** - \param x0 X-coordinate of the starting curve point - \param y0 Y-coordinate of the starting curve point - \param u0 X-coordinate of the starting velocity - \param v0 Y-coordinate of the starting velocity - \param x1 X-coordinate of the ending curve point - \param y1 Y-coordinate of the ending curve point - \param u1 X-coordinate of the ending velocity - \param v1 Y-coordinate of the ending velocity - \param texture Texture image defining line pixel colors. - \param tx0 X-coordinate of the starting texture point. - \param ty0 Y-coordinate of the starting texture point. - \param tx1 X-coordinate of the ending texture point. - \param ty1 Y-coordinate of the ending texture point. - \param precision Curve drawing precision. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch if \c true, reinit hatch motif. - **/ - template<typename t> - CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0, - const int x1, const int y1, const float u1, const float v1, - const CImg<t>& texture, - const int tx0, const int ty0, const int tx1, const int ty1, - const float opacity=1, - const float precision=4, const unsigned int pattern=~0U, - const bool init_hatch=true) { - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_empty()) return *this; - if (is_overlapped(texture)) - return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch); - if (x0==x1 && y0==y1) - return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, - y0<=0?0:y0>=texture.height()?texture.height() - 1:y0),opacity); - bool ninit_hatch = init_hatch; - const float - ax = u0 + u1 + 2*(x0 - x1), - bx = 3*(x1 - x0) - 2*u0 - u1, - ay = v0 + v1 + 2*(y0 - y1), - by = 3*(y1 - y0) - 2*v0 - v1, - _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); - int ox = x0, oy = y0, otx = tx0, oty = ty0; - for (float t1 = 0; t1<1; t1+=_precision) { - const float t2 = t1*t1, t3 = t2*t1; - const int - nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0), - ny = (int)(ay*t3 + by*t2 + v0*t1 + y0), - ntx = tx0 + (int)((tx1 - tx0)*t1), - nty = ty0 + (int)((ty1 - ty0)*t1); - draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = nx; oy = ny; otx = ntx; oty = nty; - } - return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false); - } - - //! Draw a set of consecutive splines. - /** - \param points Vertices data. - \param tangents Tangents data. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - \param is_closed_set Tells if the drawn spline set is closed. - \param precision Precision of the drawing. - \param pattern An integer whose bits describe the line pattern. - \param init_hatch If \c true, init hatch motif. - **/ - template<typename tp, typename tt, typename tc> - CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents, - const tc *const color, const float opacity=1, - const bool is_closed_set=false, const float precision=4, - const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this; - bool ninit_hatch = init_hatch; - switch (points._height) { - case 0 : case 1 : - throw CImgArgumentException(_cimg_instance - "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", - cimg_instance, - points._width,points._height,points._depth,points._spectrum,points._data); - - case 2 : { - const int x0 = (int)points(0,0), y0 = (int)points(0,1); - const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); - int ox = x0, oy = y0; - float ou = u0, ov = v0; - for (unsigned int i = 1; i<points._width; ++i) { - const int x = (int)points(i,0), y = (int)points(i,1); - const float u = (float)tangents(i,0), v = (float)tangents(i,1); - draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = x; oy = y; ou = u; ov = v; - } - if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false); - } break; - default : { - const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2); - const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2); - int ox = x0, oy = y0, oz = z0; - float ou = u0, ov = v0, ow = w0; - for (unsigned int i = 1; i<points._width; ++i) { - const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2); - const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2); - draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = x; oy = y; oz = z; ou = u; ov = v; ow = w; - } - if (is_closed_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false); - } - } - return *this; - } - - //! Draw a set of consecutive splines \overloading. - /** - Similar to previous function, with the point tangents automatically estimated from the given points set. - **/ - template<typename tp, typename tc> - CImg<T>& draw_spline(const CImg<tp>& points, - const tc *const color, const float opacity=1, - const bool is_closed_set=false, const float precision=4, - const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty() || !points || points._width<2) return *this; - CImg<Tfloat> tangents; - switch (points._height) { - case 0 : case 1 : - throw CImgArgumentException(_cimg_instance - "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", - cimg_instance, - points._width,points._height,points._depth,points._spectrum,points._data); - case 2 : { - tangents.assign(points._width,points._height); - cimg_forX(points,p) { - const unsigned int - p0 = is_closed_set?(p + points._width - 1)%points._width:(p?p - 1:0), - p1 = is_closed_set?(p + 1)%points._width:(p + 1<points._width?p + 1:p); - const float - x = (float)points(p,0), - y = (float)points(p,1), - x0 = (float)points(p0,0), - y0 = (float)points(p0,1), - x1 = (float)points(p1,0), - y1 = (float)points(p1,1), - u0 = x - x0, - v0 = y - y0, - n0 = 1e-8f + cimg::hypot(u0,v0), - u1 = x1 - x, - v1 = y1 - y, - n1 = 1e-8f + cimg::hypot(u1,v1), - u = u0/n0 + u1/n1, - v = v0/n0 + v1/n1, - n = 1e-8f + cimg::hypot(u,v), - fact = 0.5f*(n0 + n1); - tangents(p,0) = (Tfloat)(fact*u/n); - tangents(p,1) = (Tfloat)(fact*v/n); - } - } break; - default : { - tangents.assign(points._width,points._height); - cimg_forX(points,p) { - const unsigned int - p0 = is_closed_set?(p + points._width - 1)%points._width:(p?p - 1:0), - p1 = is_closed_set?(p + 1)%points._width:(p + 1<points._width?p + 1:p); - const float - x = (float)points(p,0), - y = (float)points(p,1), - z = (float)points(p,2), - x0 = (float)points(p0,0), - y0 = (float)points(p0,1), - z0 = (float)points(p0,2), - x1 = (float)points(p1,0), - y1 = (float)points(p1,1), - z1 = (float)points(p1,2), - u0 = x - x0, - v0 = y - y0, - w0 = z - z0, - n0 = 1e-8f + cimg::hypot(u0,v0,w0), - u1 = x1 - x, - v1 = y1 - y, - w1 = z1 - z, - n1 = 1e-8f + cimg::hypot(u1,v1,w1), - u = u0/n0 + u1/n1, - v = v0/n0 + v1/n1, - w = w0/n0 + w1/n1, - n = 1e-8f + cimg::hypot(u,v,w), - fact = 0.5f*(n0 + n1); - tangents(p,0) = (Tfloat)(fact*u/n); - tangents(p,1) = (Tfloat)(fact*v/n); - tangents(p,2) = (Tfloat)(fact*w/n); - } - } - } - return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch); - } - - // Inner macro for drawing triangles. -#define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \ - for (int y = y0<0?0:y0, \ - xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \ - xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \ - _sxn=1, \ - _sxr=1, \ - _sxl=1, \ - _dxn = x2>x1?x2-x1:(_sxn=-1,x1 - x2), \ - _dxr = x2>x0?x2-x0:(_sxr=-1,x0 - x2), \ - _dxl = x1>x0?x1-x0:(_sxl=-1,x0 - x1), \ - _dyn = y2-y1, \ - _dyr = y2-y0, \ - _dyl = y1-y0, \ - _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ - _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ - _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ - std::min((int)(img)._height - y - 1,y2 - y)), \ - _errn = _dyn/2, \ - _errr = _dyr/2, \ - _errl = _dyl/2, \ - _rxn = _dyn?(x2-x1)/_dyn:0, \ - _rxr = _dyr?(x2-x0)/_dyr:0, \ - _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ - (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \ - _counter>=0; --_counter, ++y, \ - xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ - xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \ - (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) - -#define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \ - for (int y = y0<0?0:y0, \ - xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \ - cr = y0>=0?c0:(c0 - y0*(c2 - c0)/(y2 - y0)), \ - xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \ - cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0 - y0*(c1 - c0)/(y1 - y0))):(c1 - y1*(c2 - c1)/(y2 - y1)), \ - _sxn=1, _scn=1, \ - _sxr=1, _scr=1, \ - _sxl=1, _scl=1, \ - _dxn = x2>x1?x2-x1:(_sxn=-1,x1 - x2), \ - _dxr = x2>x0?x2-x0:(_sxr=-1,x0 - x2), \ - _dxl = x1>x0?x1-x0:(_sxl=-1,x0 - x1), \ - _dcn = c2>c1?c2-c1:(_scn=-1,c1 - c2), \ - _dcr = c2>c0?c2-c0:(_scr=-1,c0 - c2), \ - _dcl = c1>c0?c1-c0:(_scl=-1,c0 - c1), \ - _dyn = y2-y1, \ - _dyr = y2-y0, \ - _dyl = y1-y0, \ - _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ - _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ - _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ - _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ - _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ - _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ - std::min((int)(img)._height - y - 1,y2 - y)), \ - _errn = _dyn/2, _errcn = _errn, \ - _errr = _dyr/2, _errcr = _errr, \ - _errl = _dyl/2, _errcl = _errl, \ - _rxn = _dyn?(x2 - x1)/_dyn:0, \ - _rcn = _dyn?(c2 - c1)/_dyn:0, \ - _rxr = _dyr?(x2 - x0)/_dyr:0, \ - _rcr = _dyr?(c2 - c0)/_dyr:0, \ - _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ - (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ - _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \ - (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \ - _counter>=0; --_counter, ++y, \ - xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ - cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ - xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ - _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ - (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ - _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) - -#define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \ - for (int y = y0<0?0:y0, \ - xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \ - txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \ - tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \ - xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \ - txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \ - tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \ - _sxn=1, _stxn=1, _styn=1, \ - _sxr=1, _stxr=1, _styr=1, \ - _sxl=1, _stxl=1, _styl=1, \ - _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), \ - _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), \ - _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), \ - _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \ - _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \ - _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \ - _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \ - _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \ - _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \ - _dyn = y2-y1, \ - _dyr = y2-y0, \ - _dyl = y1-y0, \ - _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ - _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ - _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ - _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ - _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ - _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ - _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ - _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ - _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ - std::min((int)(img)._height - y - 1,y2 - y)), \ - _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \ - _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \ - _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \ - _rxn = _dyn?(x2 - x1)/_dyn:0, \ - _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \ - _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \ - _rxr = _dyr?(x2 - x0)/_dyr:0, \ - _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \ - _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \ - _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \ - (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ - _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \ - (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ - _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \ - (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ - _counter>=0; --_counter, ++y, \ - xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ - txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ - tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ - xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ - tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ - _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ - (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ - _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\ - _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl)) - -#define _cimg_for_triangle4(img,xl,cl,txl,tyl,xr,cr,txr,tyr,y,x0,y0,c0,tx0,ty0,x1,y1,c1,tx1,ty1,x2,y2,c2,tx2,ty2) \ - for (int y = y0<0?0:y0, \ - xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \ - cr = y0>=0?c0:(c0 - y0*(c2 - c0)/(y2 - y0)), \ - txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \ - tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \ - xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \ - cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0 - y0*(c1 - c0)/(y1 - y0))):(c1 - y1*(c2 - c1)/(y2 - y1)), \ - txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \ - tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \ - _sxn=1, _scn=1, _stxn=1, _styn=1, \ - _sxr=1, _scr=1, _stxr=1, _styr=1, \ - _sxl=1, _scl=1, _stxl=1, _styl=1, \ - _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), \ - _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), \ - _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), \ - _dcn = c2>c1?c2 - c1:(_scn=-1,c1 - c2), \ - _dcr = c2>c0?c2 - c0:(_scr=-1,c0 - c2), \ - _dcl = c1>c0?c1 - c0:(_scl=-1,c0 - c1), \ - _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \ - _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \ - _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \ - _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \ - _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \ - _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \ - _dyn = y2 - y1, \ - _dyr = y2 - y0, \ - _dyl = y1 - y0, \ - _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ - _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ - _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ - _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ - _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ - _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ - _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ - _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ - _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ - _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ - _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ - _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ - std::min((int)(img)._height - y - 1,y2 - y)), \ - _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \ - _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \ - _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \ - _rxn = _dyn?(x2 - x1)/_dyn:0, \ - _rcn = _dyn?(c2 - c1)/_dyn:0, \ - _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \ - _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \ - _rxr = _dyr?(x2 - x0)/_dyr:0, \ - _rcr = _dyr?(c2 - c0)/_dyr:0, \ - _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \ - _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \ - _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \ - (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ - _rcl = (y0!=y1 && y1>0)?(_dyl?(c1 - c0)/_dyl:0): \ - (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \ - _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \ - (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ - _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \ - (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ - _counter>=0; --_counter, ++y, \ - xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ - cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ - txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ - tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ - xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ - txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ - tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ - _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ - (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ - _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ - _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ - _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl)) - -#define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,\ - tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \ - for (int y = y0<0?0:y0, \ - xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \ - txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \ - tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \ - lxr = y0>=0?lx0:(lx0 - y0*(lx2 - lx0)/(y2 - y0)), \ - lyr = y0>=0?ly0:(ly0 - y0*(ly2 - ly0)/(y2 - y0)), \ - xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \ - txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \ - tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \ - lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0 - y0*(lx1 - lx0)/(y1 - y0))):(lx1 - y1*(lx2 - lx1)/(y2 - y1)), \ - lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0 - y0*(ly1 - ly0)/(y1 - y0))):(ly1 - y1*(ly2 - ly1)/(y2 - y1)), \ - _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \ - _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \ - _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \ - _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), _dyn = y2 - y1, \ - _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), _dyr = y2 - y0, \ - _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), _dyl = y1 - y0, \ - _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \ - _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \ - _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \ - _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \ - _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \ - _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \ - _dlxn = lx2>lx1?lx2 - lx1:(_slxn=-1,lx1 - lx2), \ - _dlxr = lx2>lx0?lx2 - lx0:(_slxr=-1,lx0 - lx2), \ - _dlxl = lx1>lx0?lx1 - lx0:(_slxl=-1,lx0 - lx1), \ - _dlyn = ly2>ly1?ly2 - ly1:(_slyn=-1,ly1 - ly2), \ - _dlyr = ly2>ly0?ly2 - ly0:(_slyr=-1,ly0 - ly2), \ - _dlyl = ly1>ly0?ly1 - ly0:(_slyl=-1,ly0 - ly1), \ - _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ - _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ - _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ - _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ - _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ - _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ - _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ - _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ - _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ - _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \ - _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \ - _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \ - _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \ - _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \ - _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \ - std::min((int)(img)._height - y - 1,y2 - y)), \ - _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \ - _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \ - _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \ - _rxn = _dyn?(x2 - x1)/_dyn:0, \ - _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \ - _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \ - _rlxn = _dyn?(lx2 - lx1)/_dyn:0, \ - _rlyn = _dyn?(ly2 - ly1)/_dyn:0, \ - _rxr = _dyr?(x2 - x0)/_dyr:0, \ - _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \ - _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \ - _rlxr = _dyr?(lx2 - lx0)/_dyr:0, \ - _rlyr = _dyr?(ly2 - ly0)/_dyr:0, \ - _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \ - (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ - _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \ - (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ - _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \ - (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \ - _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1 - lx0)/_dyl:0): \ - (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \ - _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1 - ly0)/_dyl:0): \ - (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \ - _counter>=0; --_counter, ++y, \ - xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ - txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ - tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ - lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \ - lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \ - xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ - tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ - lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \ - lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \ - _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ - (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ - _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ - _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \ - _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \ - _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl)) - - // [internal] Draw a filled triangle. - template<typename tc> - CImg<T>& _draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const tc *const color, const float opacity, - const float brightness) { - cimg_init_scanline(color,opacity); - const float nbrightness = cimg::cut(brightness,0,2); - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2); - if (ny0<height() && ny2>=0) { - if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0) - _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) - cimg_draw_scanline(xl,xr,y,color,opacity,nbrightness); - else - _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) - cimg_draw_scanline(xr,xl,y,color,opacity,nbrightness); - } - return *this; - } - - //! Draw a filled 2D triangle. - /** - \param x0 X-coordinate of the first vertex. - \param y0 Y-coordinate of the first vertex. - \param x1 X-coordinate of the second vertex. - \param y1 Y-coordinate of the second vertex. - \param x2 X-coordinate of the third vertex. - \param y2 Y-coordinate of the third vertex. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename tc> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const tc *const color, const float opacity=1) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Specified color is (null).", - cimg_instance); - _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); - return *this; - } - - //! Draw a outlined 2D triangle. - /** - \param x0 X-coordinate of the first vertex. - \param y0 Y-coordinate of the first vertex. - \param x1 X-coordinate of the second vertex. - \param y1 Y-coordinate of the second vertex. - \param x2 X-coordinate of the third vertex. - \param y2 Y-coordinate of the third vertex. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the outline pattern. - **/ - template<typename tc> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const tc *const color, const float opacity, - const unsigned int pattern) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Specified color is (null).", - cimg_instance); - draw_line(x0,y0,x1,y1,color,opacity,pattern,true). - draw_line(x1,y1,x2,y2,color,opacity,pattern,false). - draw_line(x2,y2,x0,y0,color,opacity,pattern,false); - return *this; - } - - //! Draw a filled 2D triangle, with z-buffering. - /** - \param zbuffer Z-buffer image. - \param x0 X-coordinate of the first vertex. - \param y0 Y-coordinate of the first vertex. - \param z0 Z-coordinate of the first vertex. - \param x1 X-coordinate of the second vertex. - \param y1 Y-coordinate of the second vertex. - \param z1 Z-coordinate of the second vertex. - \param x2 X-coordinate of the third vertex. - \param y2 Y-coordinate of the third vertex. - \param z2 Z-coordinate of the third vertex. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - \param brightness Brightness factor. - **/ - template<typename tz, typename tc> - CImg<T>& draw_triangle(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const tc *const color, const float opacity=1, - const float brightness=1) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Specified color is (null).", - cimg_instance); - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float - nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), - nbrightness = cimg::cut(brightness,0,2); - const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; - tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2); - if (ny0>=height() || ny2<0) return *this; - tzfloat - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); - _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { - if (y==ny1) { zl = nz1; pzl = pzn; } - int xleft = xleft0, xright = xright0; - tzfloat zleft = zl, zright = zr; - if (xright<xleft) cimg::swap(xleft,xright,zleft,zright); - const int dx = xright - xleft; - const tzfloat pentez = (zright - zleft)/dx; - if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx; - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0; - if (opacity>=1) { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } - ptrd-=offx; - } - zleft+=pentez; - } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; } - ptrd-=offx; - } - zleft+=pentez; - } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - cimg_forC(*this,c) { *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); ptrd+=whd; } - ptrd-=offx; - } - zleft+=pentez; - } - } else { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; } - ptrd-=offx; - } - zleft+=pentez; - } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; } - ptrd-=offx; - } - zleft+=pentez; - } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - cimg_forC(*this,c) { - const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; - } - ptrd-=offx; - } - zleft+=pentez; - } - } - zr+=pzr; zl+=pzl; - } - return *this; - } - - //! Draw a Gouraud-shaded 2D triangle. - /** - \param x0 X-coordinate of the first vertex in the image instance. - \param y0 Y-coordinate of the first vertex in the image instance. - \param x1 X-coordinate of the second vertex in the image instance. - \param y1 Y-coordinate of the second vertex in the image instance. - \param x2 X-coordinate of the third vertex in the image instance. - \param y2 Y-coordinate of the third vertex in the image instance. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param brightness0 Brightness factor of the first vertex (in [0,2]). - \param brightness1 brightness factor of the second vertex (in [0,2]). - \param brightness2 brightness factor of the third vertex (in [0,2]). - \param opacity Drawing opacity. - **/ - template<typename tc> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const tc *const color, - const float brightness0, - const float brightness1, - const float brightness2, - const float opacity=1) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Specified color is (null).", - cimg_instance); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd - 1; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nc0 = (int)((brightness0<0.f?0.f:(brightness0>2.f?2.f:brightness0))*256.f), - nc1 = (int)((brightness1<0.f?0.f:(brightness1>2.f?2.f:brightness1))*256.f), - nc2 = (int)((brightness2<0.f?0.f:(brightness2>2.f?2.f:brightness2))*256.f); - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2); - if (ny0>=height() || ny2<0) return *this; - _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { - int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; - if (xright<xleft) cimg::swap(xleft,xright,cleft,cright); - const int - dx = xright - xleft, - dc = cright>cleft?cright - cleft:cleft - cright, - rc = dx?(cright - cleft)/dx:0, - sc = cright>cleft?1:-1, - ndc = dc - (dx?dx*(dc/dx):0); - int errc = dx>>1; - if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx; - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y); - if (opacity>=1) for (int x = xleft; x<=xright; ++x) { - const tc *col = color; - cimg_forC(*this,c) { - *ptrd = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); - ptrd+=whd; - } - ptrd-=offx; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } else for (int x = xleft; x<=xright; ++x) { - const tc *col = color; - cimg_forC(*this,c) { - const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; - } - ptrd-=offx; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } - } - return *this; - } - - //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading. - template<typename tz, typename tc> - CImg<T>& draw_triangle(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const tc *const color, - const float brightness0, - const float brightness1, - const float brightness2, - const float opacity=1) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Specified color is (null).", - cimg_instance); - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nc0 = (int)((brightness0<0.f?0.f:(brightness0>2.f?2.f:brightness0))*256.f), - nc1 = (int)((brightness1<0.f?0.f:(brightness1>2.f?2.f:brightness1))*256.f), - nc2 = (int)((brightness2<0.f?0.f:(brightness2>2.f?2.f:brightness2))*256.f); - tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2); - if (ny0>=height() || ny2<0) return *this; - tzfloat - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); - _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { - if (y==ny1) { zl = nz1; pzl = pzn; } - int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; - tzfloat zleft = zl, zright = zr; - if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright); - const int - dx = xright - xleft, - dc = cright>cleft?cright - cleft:cleft - cright, - rc = dx?(cright - cleft)/dx:0, - sc = cright>cleft?1:-1, - ndc = dc - (dx?dx*(dc/dx):0); - const tzfloat pentez = (zright - zleft)/dx; - int errc = dx>>1; - if (xleft<0 && dx) { - cleft-=xleft*(cright - cleft)/dx; - zleft-=xleft*(zright - zleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T *ptrd = data(xleft,y); - tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0; - if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - cimg_forC(*this,c) { - *ptrd = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); - ptrd+=whd; - } - ptrd-=offx; - } - zleft+=pentez; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - cimg_forC(*this,c) { - const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; - } - ptrd-=offx; - } - zleft+=pentez; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } - zr+=pzr; zl+=pzl; - } - return *this; - } - - //! Draw a color-interpolated 2D triangle. - /** - \param x0 X-coordinate of the first vertex in the image instance. - \param y0 Y-coordinate of the first vertex in the image instance. - \param x1 X-coordinate of the second vertex in the image instance. - \param y1 Y-coordinate of the second vertex in the image instance. - \param x2 X-coordinate of the third vertex in the image instance. - \param y2 Y-coordinate of the third vertex in the image instance. - \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex. - \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the seconf vertex. - \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex. - \param opacity Drawing opacity. - **/ - template<typename tc1, typename tc2, typename tc3> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const tc1 *const color1, - const tc2 *const color2, - const tc3 *const color3, - const float opacity=1) { - const unsigned char one = 1; - cimg_forC(*this,c) - get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity); - return *this; - } - - //! Draw a textured 2D triangle. - /** - \param x0 X-coordinate of the first vertex in the image instance. - \param y0 Y-coordinate of the first vertex in the image instance. - \param x1 X-coordinate of the second vertex in the image instance. - \param y1 Y-coordinate of the second vertex in the image instance. - \param x2 X-coordinate of the third vertex in the image instance. - \param y2 Y-coordinate of the third vertex in the image instance. - \param texture Texture image used to fill the triangle. - \param tx0 X-coordinate of the first vertex in the texture image. - \param ty0 Y-coordinate of the first vertex in the texture image. - \param tx1 X-coordinate of the second vertex in the texture image. - \param ty1 Y-coordinate of the second vertex in the texture image. - \param tx2 X-coordinate of the third vertex in the texture image. - \param ty2 Y-coordinate of the third vertex in the texture image. - \param opacity Drawing opacity. - \param brightness Brightness factor of the drawing (in [0,2]). - **/ - template<typename tc> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const float opacity=1, - const float brightness=1) { - if (is_empty()) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) - return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); - static const T maxval = (T)std::min(cimg::type<T>::max(),cimg::type<tc>::max()); - const float - nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), - nbrightness = cimg::cut(brightness,0,2); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - offx = _spectrum*whd - 1; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2); - if (ny0>=height() || ny2<0) return *this; - _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y, - nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) { - int - xleft = xleft0, xright = xright0, - txleft = txleft0, txright = txright0, - tyleft = tyleft0, tyright = tyright0; - if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright); - const int - dx = xright - xleft, - dtx = txright>txleft?txright - txleft:txleft - txright, - dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, - rtx = dx?(txright - txleft)/dx:0, - rty = dx?(tyright - tyleft)/dx:0, - stx = txright>txleft?1:-1, - sty = tyright>tyleft?1:-1, - ndtx = dtx - (dx?dx*(dtx/dx):0), - ndty = dty - (dx?dx*(dty/dx):0); - int errtx = dx>>1, errty = errtx; - if (xleft<0 && dx) { - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - if (opacity>=1) { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)*col; - ptrd+=whd; col+=twh; - } - ptrd-=offx; - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } else for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } - } else { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } else for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } - } - } - return *this; - } - - //! Draw a 2D textured triangle, with perspective correction. - template<typename tc> - CImg<T>& draw_triangle(const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const float opacity=1, - const float brightness=1) { - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) - return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float - nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), - nbrightness = cimg::cut(brightness,0,2); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - offx = _spectrum*whd - 1; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; - float - ntx0 = tx0/z0, nty0 = ty0/z0, - ntx1 = tx1/z1, nty1 = ty1/z1, - ntx2 = tx2/z2, nty2 = ty2/z2, - nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2); - if (ny0>=height() || ny2<0) return *this; - float - ptxl = (ntx1 - ntx0)/(ny1 - ny0), - ptxr = (ntx2 - ntx0)/(ny2 - ny0), - ptxn = (ntx2 - ntx1)/(ny2 - ny1), - ptyl = (nty1 - nty0)/(ny1 - ny0), - ptyr = (nty2 - nty0)/(ny2 - ny0), - ptyn = (nty2 - nty1)/(ny2 - ny1), - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), - tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), - txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))): - (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), - tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))): - (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); - _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { - if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } - int xleft = xleft0, xright = xright0; - float - zleft = zl, zright = zr, - txleft = txl, txright = txr, - tyleft = tyl, tyright = tyr; - if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright); - const int dx = xright - xleft; - const float - pentez = (zright - zleft)/dx, - pentetx = (txright - txleft)/dx, - pentety = (tyright - tyleft)/dx; - if (xleft<0 && dx) { - zleft-=xleft*(zright - zleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - if (opacity>=1) { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)*col; - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } - } else { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } - } - zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; - } - return *this; - } - - //! Draw a textured 2D triangle, with perspective correction and z-buffering. - template<typename tz, typename tc> - CImg<T>& draw_triangle(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const float opacity=1, - const float brightness=1) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) - return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float - nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), - nbrightness = cimg::cut(brightness,0,2); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - offx = _spectrum*whd; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; - float - ntx0 = tx0/z0, nty0 = ty0/z0, - ntx1 = tx1/z1, nty1 = ty1/z1, - ntx2 = tx2/z2, nty2 = ty2/z2; - tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2); - if (ny0>=height() || ny2<0) return *this; - float - ptxl = (ntx1 - ntx0)/(ny1 - ny0), - ptxr = (ntx2 - ntx0)/(ny2 - ny0), - ptxn = (ntx2 - ntx1)/(ny2 - ny1), - ptyl = (nty1 - nty0)/(ny1 - ny0), - ptyr = (nty2 - nty0)/(ny2 - ny0), - ptyn = (nty2 - nty1)/(ny2 - ny1), - txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), - tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), - txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))): - (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), - tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))): - (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); - tzfloat - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); - _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { - if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } - int xleft = xleft0, xright = xright0; - float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; - tzfloat zleft = zl, zright = zr; - if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright); - const int dx = xright - xleft; - const float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; - const tzfloat pentez = (zright - zleft)/dx; - if (xleft<0 && dx) { - zleft-=xleft*(zright - zleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T *ptrd = data(xleft,y,0,0); - tz *ptrz = zbuffer.data(xleft,y); - if (opacity>=1) { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)*col; - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } - } else { - if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - } - } - zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; - } - return *this; - } - - //! Draw a Phong-shaded 2D triangle. - /** - \param x0 X-coordinate of the first vertex in the image instance. - \param y0 Y-coordinate of the first vertex in the image instance. - \param x1 X-coordinate of the second vertex in the image instance. - \param y1 Y-coordinate of the second vertex in the image instance. - \param x2 X-coordinate of the third vertex in the image instance. - \param y2 Y-coordinate of the third vertex in the image instance. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param light Light image. - \param lx0 X-coordinate of the first vertex in the light image. - \param ly0 Y-coordinate of the first vertex in the light image. - \param lx1 X-coordinate of the second vertex in the light image. - \param ly1 Y-coordinate of the second vertex in the light image. - \param lx2 X-coordinate of the third vertex in the light image. - \param ly2 Y-coordinate of the third vertex in the light image. - \param opacity Drawing opacity. - **/ - template<typename tc, typename tl> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const tc *const color, - const CImg<tl>& light, - const int lx0, const int ly0, - const int lx1, const int ly1, - const int lx2, const int ly2, - const float opacity=1) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Specified color is (null).", - cimg_instance); - if (light._depth>1 || light._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", - cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); - if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; - const ulongT - whd = (ulongT)_width*_height*_depth, - lwh = (ulongT)light._width*light._height, - offx = _spectrum*whd - 1; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2); - if (ny0>=height() || ny2<0) return *this; - _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, - nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { - int - xleft = xleft0, xright = xright0, - lxleft = lxleft0, lxright = lxright0, - lyleft = lyleft0, lyright = lyright0; - if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright); - const int - dx = xright - xleft, - dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright, - dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, - rlx = dx?(lxright - lxleft)/dx:0, - rly = dx?(lyright - lyleft)/dx:0, - slx = lxright>lxleft?1:-1, - sly = lyright>lyleft?1:-1, - ndlx = dlx - (dx?dx*(dlx/dx):0), - ndly = dly - (dx?dx*(dly/dx):0); - int errlx = dx>>1, errly = errlx; - if (xleft<0 && dx) { - lxleft-=xleft*(lxright - lxleft)/dx; - lyleft-=xleft*(lyright - lyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - if (opacity>=1) for (int x = xleft; x<=xright; ++x) { - const tc *col = color; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval)); - ptrd+=whd; lig+=lwh; - } - ptrd-=offx; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } else for (int x = xleft; x<=xright; ++x) { - const tc *col = color; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval)); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; lig+=lwh; - } - ptrd-=offx; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } - } - return *this; - } - - //! Draw a Phong-shaded 2D triangle, with z-buffering. - template<typename tz, typename tc, typename tl> - CImg<T>& draw_triangle(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const tc *const color, - const CImg<tl>& light, - const int lx0, const int ly0, - const int lx1, const int ly1, - const int lx2, const int ly2, - const float opacity=1) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Specified color is (null).", - cimg_instance); - if (light._depth>1 || light._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", - cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color, - +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT - whd = (ulongT)_width*_height*_depth, - lwh = (ulongT)light._width*light._height, - offx = _spectrum*whd; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; - tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2); - if (ny0>=height() || ny2<0) return *this; - tzfloat - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); - _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, - nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { - if (y==ny1) { zl = nz1; pzl = pzn; } - int - xleft = xleft0, xright = xright0, - lxleft = lxleft0, lxright = lxright0, - lyleft = lyleft0, lyright = lyright0; - tzfloat zleft = zl, zright = zr; - if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright); - const int - dx = xright - xleft, - dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright, - dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, - rlx = dx?(lxright - lxleft)/dx:0, - rly = dx?(lyright - lyleft)/dx:0, - slx = lxright>lxleft?1:-1, - sly = lyright>lyleft?1:-1, - ndlx = dlx - (dx?dx*(dlx/dx):0), - ndly = dly - (dx?dx*(dly/dx):0); - const tzfloat pentez = (zright - zleft)/dx; - int errlx = dx>>1, errly = errlx; - if (xleft<0 && dx) { - zleft-=xleft*(zright - zleft)/dx; - lxleft-=xleft*(lxright - lxleft)/dx; - lyleft-=xleft*(lyright - lyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T *ptrd = data(xleft,y,0,0); - tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0; - if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const tc cval = *(col++); - *ptrd = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval); - ptrd+=whd; lig+=lwh; - } - ptrd-=offx; - } - zleft+=pentez; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tc *col = color; - const tl *lig = &light._atXY(lxleft,lyleft); - cimg_forC(*this,c) { - const tl l = *lig; - const tc cval = *(col++); - const T val = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; lig+=lwh; - } - ptrd-=offx; - } - zleft+=pentez; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } - zr+=pzr; zl+=pzl; - } - return *this; - } - - //! Draw a textured Gouraud-shaded 2D triangle. - /** - \param x0 X-coordinate of the first vertex in the image instance. - \param y0 Y-coordinate of the first vertex in the image instance. - \param x1 X-coordinate of the second vertex in the image instance. - \param y1 Y-coordinate of the second vertex in the image instance. - \param x2 X-coordinate of the third vertex in the image instance. - \param y2 Y-coordinate of the third vertex in the image instance. - \param texture Texture image used to fill the triangle. - \param tx0 X-coordinate of the first vertex in the texture image. - \param ty0 Y-coordinate of the first vertex in the texture image. - \param tx1 X-coordinate of the second vertex in the texture image. - \param ty1 Y-coordinate of the second vertex in the texture image. - \param tx2 X-coordinate of the third vertex in the texture image. - \param ty2 Y-coordinate of the third vertex in the texture image. - \param brightness0 Brightness factor of the first vertex. - \param brightness1 Brightness factor of the second vertex. - \param brightness2 Brightness factor of the third vertex. - \param opacity Drawing opacity. - **/ - template<typename tc> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const float brightness0, - const float brightness1, - const float brightness2, - const float opacity=1) { - if (is_empty()) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) - return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, - brightness0,brightness1,brightness2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - offx = _spectrum*whd - 1; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, - nc0 = (int)((brightness0<0.f?0.f:(brightness0>2.f?2.f:brightness0))*256.f), - nc1 = (int)((brightness1<0.f?0.f:(brightness1>2.f?2.f:brightness1))*256.f), - nc2 = (int)((brightness2<0.f?0.f:(brightness2>2.f?2.f:brightness2))*256.f); - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2); - if (ny0>=height() || ny2<0) return *this; - _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y, - nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) { - int - xleft = xleft0, xright = xright0, - cleft = cleft0, cright = cright0, - txleft = txleft0, txright = txright0, - tyleft = tyleft0, tyright = tyright0; - if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright); - const int - dx = xright - xleft, - dc = cright>cleft?cright - cleft:cleft - cright, - dtx = txright>txleft?txright - txleft:txleft - txright, - dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, - rc = dx?(cright - cleft)/dx:0, - rtx = dx?(txright - txleft)/dx:0, - rty = dx?(tyright - tyleft)/dx:0, - sc = cright>cleft?1:-1, - stx = txright>txleft?1:-1, - sty = tyright>tyleft?1:-1, - ndc = dc - (dx?dx*(dc/dx):0), - ndtx = dtx - (dx?dx*(dtx/dx):0), - ndty = dty - (dx?dx*(dty/dx):0); - int errc = dx>>1, errtx = errc, errty = errc; - if (xleft<0 && dx) { - cleft-=xleft*(cright - cleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - if (opacity>=1) for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } else for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - cimg_forC(*this,c) { - const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } - } - return *this; - } - - //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading. - template<typename tc> - CImg<T>& draw_triangle(const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const float brightness0, - const float brightness1, - const float brightness2, - const float opacity=1) { - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, - brightness0,brightness1,brightness2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - offx = _spectrum*whd - 1; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nc0 = (int)((brightness0<0.f?0.f:(brightness0>2.f?2.f:brightness0))*256.f), - nc1 = (int)((brightness1<0.f?0.f:(brightness1>2.f?2.f:brightness1))*256.f), - nc2 = (int)((brightness2<0.f?0.f:(brightness2>2.f?2.f:brightness2))*256.f); - float - ntx0 = tx0/z0, nty0 = ty0/z0, - ntx1 = tx1/z1, nty1 = ty1/z1, - ntx2 = tx2/z2, nty2 = ty2/z2, - nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2); - if (ny0>=height() || ny2<0) return *this; - float - ptxl = (ntx1 - ntx0)/(ny1 - ny0), - ptxr = (ntx2 - ntx0)/(ny2 - ny0), - ptxn = (ntx2 - ntx1)/(ny2 - ny1), - ptyl = (nty1 - nty0)/(ny1 - ny0), - ptyr = (nty2 - nty0)/(ny2 - ny0), - ptyn = (nty2 - nty1)/(ny2 - ny1), - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), - tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), - txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))): - (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), - tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))): - (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); - _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { - if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } - int - xleft = xleft0, xright = xright0, - cleft = cleft0, cright = cright0; - float - zleft = zl, zright = zr, - txleft = txl, txright = txr, - tyleft = tyl, tyright = tyr; - if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright); - const int - dx = xright - xleft, - dc = cright>cleft?cright - cleft:cleft - cright, - rc = dx?(cright - cleft)/dx:0, - sc = cright>cleft?1:-1, - ndc = dc - (dx?dx*(dc/dx):0); - const float - pentez = (zright - zleft)/dx, - pentetx = (txright - txleft)/dx, - pentety = (tyright - tyleft)/dx; - int errc = dx>>1; - if (xleft<0 && dx) { - cleft-=xleft*(cright - cleft)/dx; - zleft-=xleft*(zright - zleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - if (opacity>=1) for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } else for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } - zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; - } - return *this; - } - - //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading. - template<typename tz, typename tc> - CImg<T>& draw_triangle(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const float brightness0, - const float brightness1, - const float brightness2, - const float opacity=1) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (is_overlapped(texture)) - return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, - brightness0,brightness1,brightness2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - offx = _spectrum*whd; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nc0 = (int)((brightness0<0.f?0.f:(brightness0>2.f?2.f:brightness0))*256.f), - nc1 = (int)((brightness1<0.f?0.f:(brightness1>2.f?2.f:brightness1))*256.f), - nc2 = (int)((brightness2<0.f?0.f:(brightness2>2.f?2.f:brightness2))*256.f); - float - ntx0 = tx0/z0, nty0 = ty0/z0, - ntx1 = tx1/z1, nty1 = ty1/z1, - ntx2 = tx2/z2, nty2 = ty2/z2; - tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2); - if (ny0>=height() || ny2<0) return *this; - float - ptxl = (ntx1 - ntx0)/(ny1 - ny0), - ptxr = (ntx2 - ntx0)/(ny2 - ny0), - ptxn = (ntx2 - ntx1)/(ny2 - ny1), - ptyl = (nty1 - nty0)/(ny1 - ny0), - ptyr = (nty2 - nty0)/(ny2 - ny0), - ptyn = (nty2 - nty1)/(ny2 - ny1), - txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), - tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), - txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))): - (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), - tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))): - (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); - tzfloat - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); - _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { - if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } - int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; - float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; - tzfloat zleft = zl, zright = zr; - if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright); - const int - dx = xright - xleft, - dc = cright>cleft?cright - cleft:cleft - cright, - rc = dx?(cright - cleft)/dx:0, - sc = cright>cleft?1:-1, - ndc = dc - (dx?dx*(dc/dx):0); - float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; - const tzfloat pentez = (zright - zleft)/dx; - int errc = dx>>1; - if (xleft<0 && dx) { - cleft-=xleft*(cright - cleft)/dx; - zleft-=xleft*(zright - zleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y); - tz *ptrz = zbuffer.data(xleft,y); - if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - cimg_forC(*this,c) { - const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); - } - zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; - } - return *this; - } - - //! Draw a textured Phong-shaded 2D triangle. - /** - \param x0 X-coordinate of the first vertex in the image instance. - \param y0 Y-coordinate of the first vertex in the image instance. - \param x1 X-coordinate of the second vertex in the image instance. - \param y1 Y-coordinate of the second vertex in the image instance. - \param x2 X-coordinate of the third vertex in the image instance. - \param y2 Y-coordinate of the third vertex in the image instance. - \param texture Texture image used to fill the triangle. - \param tx0 X-coordinate of the first vertex in the texture image. - \param ty0 Y-coordinate of the first vertex in the texture image. - \param tx1 X-coordinate of the second vertex in the texture image. - \param ty1 Y-coordinate of the second vertex in the texture image. - \param tx2 X-coordinate of the third vertex in the texture image. - \param ty2 Y-coordinate of the third vertex in the texture image. - \param light Light image. - \param lx0 X-coordinate of the first vertex in the light image. - \param ly0 Y-coordinate of the first vertex in the light image. - \param lx1 X-coordinate of the second vertex in the light image. - \param ly1 Y-coordinate of the second vertex in the light image. - \param lx2 X-coordinate of the third vertex in the light image. - \param ly2 Y-coordinate of the third vertex in the light image. - \param opacity Drawing opacity. - **/ - template<typename tc, typename tl> - CImg<T>& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const CImg<tl>& light, - const int lx0, const int ly0, - const int lx1, const int ly1, - const int lx2, const int ly2, - const float opacity=1) { - if (is_empty()) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (light._depth>1 || light._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", - cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); - if (is_overlapped(texture)) - return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - if (is_overlapped(light)) - return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - lwh = (ulongT)light._width*light._height, - offx = _spectrum*whd - 1; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, - nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2); - if (ny0>=height() || ny2<0) return *this; - const bool is_bump = texture._spectrum>=_spectrum + 2; - const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1); - - _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y, - nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) { - int - xleft = xleft0, xright = xright0, - lxleft = lxleft0, lxright = lxright0, - lyleft = lyleft0, lyright = lyright0, - txleft = txleft0, txright = txright0, - tyleft = tyleft0, tyright = tyright0; - if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright); - const int - dx = xright - xleft, - dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright, - dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, - dtx = txright>txleft?txright - txleft:txleft - txright, - dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, - rlx = dx?(lxright - lxleft)/dx:0, - rly = dx?(lyright - lyleft)/dx:0, - rtx = dx?(txright - txleft)/dx:0, - rty = dx?(tyright - tyleft)/dx:0, - slx = lxright>lxleft?1:-1, - sly = lyright>lyleft?1:-1, - stx = txright>txleft?1:-1, - sty = tyright>tyleft?1:-1, - ndlx = dlx - (dx?dx*(dlx/dx):0), - ndly = dly - (dx?dx*(dly/dx):0), - ndtx = dtx - (dx?dx*(dtx/dx):0), - ndty = dty - (dx?dx*(dty/dx):0); - int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx; - if (xleft<0 && dx) { - lxleft-=xleft*(lxright - lxleft)/dx; - lyleft-=xleft*(lyright - lyleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - if (opacity>=1) for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0; - const tl *lig = &light._atXY(lxleft + bx,lyleft + by); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - ptrd+=whd; col+=twh; lig+=lwh; - } - ptrd-=offx; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } else for (int x = xleft; x<=xright; ++x) { - const tc *col = &texture._atXY(txleft,tyleft); - const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0; - const tl *lig = &light._atXY(lxleft + bx,lyleft + by); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; lig+=lwh; - } - ptrd-=offx; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); - tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); - } - } - return *this; - } - - //! Draw a textured Phong-shaded 2D triangle, with perspective correction. - template<typename tc, typename tl> - CImg<T>& draw_triangle(const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const CImg<tl>& light, - const int lx0, const int ly0, - const int lx1, const int ly1, - const int lx2, const int ly2, - const float opacity=1) { - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (light._depth>1 || light._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", - cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); - if (is_overlapped(texture)) - return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, - light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - if (is_overlapped(light)) - return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2, - +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - lwh = (ulongT)light._width*light._height, - offx = _spectrum*whd - 1; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; - float - ntx0 = tx0/z0, nty0 = ty0/z0, - ntx1 = tx1/z1, nty1 = ty1/z1, - ntx2 = tx2/z2, nty2 = ty2/z2, - nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2); - if (ny0>=height() || ny2<0) return *this; - float - ptxl = (ntx1 - ntx0)/(ny1 - ny0), - ptxr = (ntx2 - ntx0)/(ny2 - ny0), - ptxn = (ntx2 - ntx1)/(ny2 - ny1), - ptyl = (nty1 - nty0)/(ny1 - ny0), - ptyr = (nty2 - nty0)/(ny2 - ny0), - ptyn = (nty2 - nty1)/(ny2 - ny1), - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), - tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), - txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))): - (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), - tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))): - (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); - const bool is_bump = texture._spectrum>=_spectrum + 2; - const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1); - - _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, - nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { - if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } - int - xleft = xleft0, xright = xright0, - lxleft = lxleft0, lxright = lxright0, - lyleft = lyleft0, lyright = lyright0; - float - zleft = zl, zright = zr, - txleft = txl, txright = txr, - tyleft = tyl, tyright = tyr; - if (xright<xleft) - cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright); - const int - dx = xright - xleft, - dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright, - dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, - rlx = dx?(lxright - lxleft)/dx:0, - rly = dx?(lyright - lyleft)/dx:0, - slx = lxright>lxleft?1:-1, - sly = lyright>lyleft?1:-1, - ndlx = dlx - (dx?dx*(dlx/dx):0), - ndly = dly - (dx?dx*(dly/dx):0); - const float - pentez = (zright - zleft)/dx, - pentetx = (txright - txleft)/dx, - pentety = (tyright - tyleft)/dx; - int errlx = dx>>1, errly = errlx; - if (xleft<0 && dx) { - zleft-=xleft*(zright - zleft)/dx; - lxleft-=xleft*(lxright - lxleft)/dx; - lyleft-=xleft*(lyright - lyleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y,0,0); - if (opacity>=1) for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0; - const tl *lig = &light._atXY(lxleft + bx,lyleft + by); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - ptrd+=whd; col+=twh; lig+=lwh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } else for (int x = xleft; x<=xright; ++x) { - const float invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0; - const tl *lig = &light._atXY(lxleft + bx,lyleft + by); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; lig+=lwh; - } - ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } - zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; - } - return *this; - } - - //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering. - template<typename tz, typename tc, typename tl> - CImg<T>& draw_triangle(CImg<tz>& zbuffer, - const int x0, const int y0, const float z0, - const int x1, const int y1, const float z1, - const int x2, const int y2, const float z2, - const CImg<tc>& texture, - const int tx0, const int ty0, - const int tx1, const int ty1, - const int tx2, const int ty2, - const CImg<tl>& light, - const int lx0, const int ly0, - const int lx1, const int ly1, - const int lx2, const int ly2, - const float opacity=1) { - typedef typename cimg::superset<tz,float>::type tzfloat; - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (!is_sameXY(zbuffer)) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " - "different dimensions.", - cimg_instance, - zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); - if (texture._depth>1 || texture._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", - cimg_instance, - texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - if (light._depth>1 || light._spectrum<_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", - cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); - if (is_overlapped(texture)) - return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, - +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - if (is_overlapped(light)) - return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, - texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT - whd = (ulongT)_width*_height*_depth, - twh = (ulongT)texture._width*texture._height, - lwh = (ulongT)light._width*light._height, - offx = _spectrum*whd; - int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, - nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; - float - ntx0 = tx0/z0, nty0 = ty0/z0, - ntx1 = tx1/z1, nty1 = ty1/z1, - ntx2 = tx2/z2, nty2 = ty2/z2; - tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2; - if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1); - if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2); - if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2); - if (ny0>=height() || ny2<0) return *this; - float - ptxl = (ntx1 - ntx0)/(ny1 - ny0), - ptxr = (ntx2 - ntx0)/(ny2 - ny0), - ptxn = (ntx2 - ntx1)/(ny2 - ny1), - ptyl = (nty1 - nty0)/(ny1 - ny0), - ptyr = (nty2 - nty0)/(ny2 - ny0), - ptyn = (nty2 - nty1)/(ny2 - ny1), - txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), - tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), - txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))): - (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), - tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))): - (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); - tzfloat - pzl = (nz1 - nz0)/(ny1 - ny0), - pzr = (nz2 - nz0)/(ny2 - ny0), - pzn = (nz2 - nz1)/(ny2 - ny1), - zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), - zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); - const bool is_bump = texture._spectrum>=_spectrum + 2; - const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1); - - _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, - nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { - if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } - int - xleft = xleft0, xright = xright0, - lxleft = lxleft0, lxright = lxright0, - lyleft = lyleft0, lyright = lyright0; - float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; - tzfloat zleft = zl, zright = zr; - if (xright<xleft) - cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright); - const int - dx = xright - xleft, - dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright, - dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, - rlx = dx?(lxright - lxleft)/dx:0, - rly = dx?(lyright - lyleft)/dx:0, - slx = lxright>lxleft?1:-1, - sly = lyright>lyleft?1:-1, - ndlx = dlx - (dx?dx*(dlx/dx):0), - ndly = dly - (dx?dx*(dly/dx):0); - float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx; - const tzfloat pentez = (zright - zleft)/dx; - int errlx = dx>>1, errly = errlx; - if (xleft<0 && dx) { - zleft-=xleft*(zright - zleft)/dx; - lxleft-=xleft*(lxright - lxleft)/dx; - lyleft-=xleft*(lyright - lyleft)/dx; - txleft-=xleft*(txright - txleft)/dx; - tyleft-=xleft*(tyright - tyleft)/dx; - } - if (xleft<0) xleft = 0; - if (xright>=width() - 1) xright = width() - 1; - T* ptrd = data(xleft,y); - tz *ptrz = zbuffer.data(xleft,y); - if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0; - const tl *lig = &light._atXY(lxleft + bx,lyleft + by); - cimg_forC(*this,c) { - const tl l = *lig; - *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - ptrd+=whd; col+=twh; lig+=lwh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { - if (zleft>=(tzfloat)*ptrz) { - *ptrz = (tz)zleft; - const tzfloat invz = 1/zleft; - const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); - const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0; - const tl *lig = &light._atXY(lxleft + bx,lyleft + by); - cimg_forC(*this,c) { - const tl l = *lig; - const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); - *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twh; lig+=lwh; - } - ptrd-=offx; - } - zleft+=pentez; txleft+=pentetx; tyleft+=pentety; - lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); - lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); - } - zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; - } - return *this; - } - - //! Draw a filled 4D rectangle. - /** - \param x0 X-coordinate of the upper-left rectangle corner. - \param y0 Y-coordinate of the upper-left rectangle corner. - \param z0 Z-coordinate of the upper-left rectangle corner. - \param c0 C-coordinate of the upper-left rectangle corner. - \param x1 X-coordinate of the lower-right rectangle corner. - \param y1 Y-coordinate of the lower-right rectangle corner. - \param z1 Z-coordinate of the lower-right rectangle corner. - \param c1 C-coordinate of the lower-right rectangle corner. - \param val Scalar value used to fill the rectangle area. - \param opacity Drawing opacity. - **/ - CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0, - const int x1, const int y1, const int z1, const int c1, - const T val, const float opacity=1) { - if (is_empty()) return *this; - const int - nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0, - ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0, - nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0, - nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0; - const int - lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0), - lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0), - lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0), - lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0); - const ulongT - offX = (ulongT)_width - lX, - offY = (ulongT)_width*(_height - lY), - offZ = (ulongT)_width*_height*(_depth - lZ); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0); - if (lX>0 && lY>0 && lZ>0 && lC>0) - for (int v = 0; v<lC; ++v) { - for (int z = 0; z<lZ; ++z) { - for (int y = 0; y<lY; ++y) { - if (opacity>=1) { - if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; } - else { std::memset(ptrd,(int)val,lX); ptrd+=_width; } - } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; } - } - ptrd+=offY; - } - ptrd+=offZ; - } - return *this; - } - - //! Draw a filled 3D rectangle. - /** - \param x0 X-coordinate of the upper-left rectangle corner. - \param y0 Y-coordinate of the upper-left rectangle corner. - \param z0 Z-coordinate of the upper-left rectangle corner. - \param x1 X-coordinate of the lower-right rectangle corner. - \param y1 Y-coordinate of the lower-right rectangle corner. - \param z1 Z-coordinate of the lower-right rectangle corner. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename tc> - CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, - const int x1, const int y1, const int z1, - const tc *const color, const float opacity=1) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_rectangle(): Specified color is (null).", - cimg_instance); - cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity); - return *this; - } - - //! Draw an outlined 3D rectangle \overloading. - template<typename tc> - CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, - const int x1, const int y1, const int z1, - const tc *const color, const float opacity, - const unsigned int pattern) { - return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true). - draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false). - draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false). - draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false). - draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true). - draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false). - draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false). - draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false). - draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true). - draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true). - draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true). - draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); - } - - //! Draw a filled 2D rectangle. - /** - \param x0 X-coordinate of the upper-left rectangle corner. - \param y0 Y-coordinate of the upper-left rectangle corner. - \param x1 X-coordinate of the lower-right rectangle corner. - \param y1 Y-coordinate of the lower-right rectangle corner. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename tc> - CImg<T>& draw_rectangle(const int x0, const int y0, - const int x1, const int y1, - const tc *const color, const float opacity=1) { - return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity); - } - - //! Draw a outlined 2D rectangle \overloading. - template<typename tc> - CImg<T>& draw_rectangle(const int x0, const int y0, - const int x1, const int y1, - const tc *const color, const float opacity, - const unsigned int pattern) { - if (is_empty()) return *this; - if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true); - if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true); - const int - nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0, - ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0; - if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true). - draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false); - return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true). - draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false). - draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false). - draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false); - } - - //! Draw a filled 2D polygon. - /** - \param points Set of polygon vertices. - \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename tp, typename tc> - CImg<T>& draw_polygon(const CImg<tp>& points, - const tc *const color, const float opacity=1) { - if (is_empty() || !points) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_polygon(): Specified color is (null).", - cimg_instance); - if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity); - if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1), - (int)points(1,0),(int)points(1,1),color,opacity); - if (points._width==3) return draw_triangle((int)points(0,0),(int)points(0,1), - (int)points(1,0),(int)points(1,1), - (int)points(2,0),(int)points(2,1),color,opacity); - cimg_init_scanline(color,opacity); - int - xmin = 0, ymin = 0, - xmax = points.get_shared_row(0).max_min(xmin), - ymax = points.get_shared_row(1).max_min(ymin); - if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; - if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity); - - ymin = std::max(0,ymin); - ymax = std::min(height() - 1,ymax); - CImg<intT> Xs(points._width,ymax - ymin + 1); - CImg<uintT> count(Xs._height,1,1,1,0); - unsigned int n = 0, nn = 1; - bool go_on = true; - - while (go_on) { - unsigned int an = (nn + 1)%points._width; - const int - x0 = (int)points(n,0), - y0 = (int)points(n,1); - if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; } - const int - x1 = (int)points(nn,0), - y1 = (int)points(nn,1); - unsigned int tn = an; - while (points(tn,1)==y1) (tn+=1)%=points._width; - - if (y0!=y1) { - const int - y2 = (int)points(tn,1), - x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1, - dy = cimg::sign(y01), - tmax = std::max(1,cimg::abs(y01)), - tend = tmax - (dy==cimg::sign(y12)); - unsigned int y = (unsigned int)y0 - ymin; - for (int t = 0; t<=tend; ++t, y+=dy) - if (y<Xs._height) Xs(count[y]++,y) = x0 + t*x01/tmax; - } - - go_on = nn>n; - n = nn; - nn = an; - } - - cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*32)) - cimg_forY(Xs,y) { - const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort(); - int px = width(); - for (unsigned int n = 0; n<Xsy._width; n+=2) { - int x0 = Xsy[n]; - const int x1 = Xsy[n + 1]; - x0+=x0==px; - cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1); - px = x1; - } - } - return *this; - } - - //! Draw a outlined 2D polygon \overloading. - template<typename t, typename tc> - CImg<T>& draw_polygon(const CImg<t>& points, - const tc *const color, const float opacity, const unsigned int pattern) { - if (is_empty() || !points || points._width<3) return *this; - bool ninit_hatch = true; - switch (points._height) { - case 0 : case 1 : - throw CImgArgumentException(_cimg_instance - "draw_polygon(): Invalid specified point set.", - cimg_instance); - case 2 : { // 2D version - CImg<intT> npoints(points._width,2); - int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1); - unsigned int nb_points = 1; - for (unsigned int p = 1; p<points._width; ++p) { - const int nx = (int)points(p,0), ny = (int)points(p,1); - if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; } - } - const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1); - int ox = x0, oy = y0; - for (unsigned int i = 1; i<nb_points; ++i) { - const int x = (int)npoints(i,0), y = (int)npoints(i,1); - draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = x; oy = y; - } - draw_line(ox,oy,x0,y0,color,opacity,pattern,false); - } break; - default : { // 3D version - CImg<intT> npoints(points._width,3); - int - x = npoints(0,0) = (int)points(0,0), - y = npoints(0,1) = (int)points(0,1), - z = npoints(0,2) = (int)points(0,2); - unsigned int nb_points = 1; - for (unsigned int p = 1; p<points._width; ++p) { - const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2); - if (nx!=x || ny!=y || nz!=z) { - npoints(nb_points,0) = nx; npoints(nb_points,1) = ny; npoints(nb_points++,2) = nz; - x = nx; y = ny; z = nz; - } - } - const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2); - int ox = x0, oy = y0, oz = z0; - for (unsigned int i = 1; i<nb_points; ++i) { - const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2); - draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch); - ninit_hatch = false; - ox = x; oy = y; oz = z; - } - draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false); - } - } - return *this; - } - - //! Draw a filled 2D ellipse. - /** - \param x0 X-coordinate of the ellipse center. - \param y0 Y-coordinate of the ellipse center. - \param r1 First radius of the ellipse. - \param r2 Second radius of the ellipse. - \param angle Angle of the first radius. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename tc> - CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, - const tc *const color, const float opacity=1) { - return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U); - } - - //! Draw a filled 2D ellipse \overloading. - /** - \param x0 X-coordinate of the ellipse center. - \param y0 Y-coordinate of the ellipse center. - \param tensor Diffusion tensor describing the ellipse. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename t, typename tc> - CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor, - const tc *const color, const float opacity=1) { - CImgList<t> eig = tensor.get_symmetric_eigen(); - const CImg<t> &val = eig[0], &vec = eig[1]; - return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), - std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, - color,opacity); - } - - //! Draw an outlined 2D ellipse. - /** - \param x0 X-coordinate of the ellipse center. - \param y0 Y-coordinate of the ellipse center. - \param r1 First radius of the ellipse. - \param r2 Second radius of the ellipse. - \param angle Angle of the first radius. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the outline pattern. - **/ - template<typename tc> - CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, - const tc *const color, const float opacity, const unsigned int pattern) { - if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern); - return *this; - } - - //! Draw an outlined 2D ellipse \overloading. - /** - \param x0 X-coordinate of the ellipse center. - \param y0 Y-coordinate of the ellipse center. - \param tensor Diffusion tensor describing the ellipse. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the outline pattern. - **/ - template<typename t, typename tc> - CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor, - const tc *const color, const float opacity, - const unsigned int pattern) { - CImgList<t> eig = tensor.get_symmetric_eigen(); - const CImg<t> &val = eig[0], &vec = eig[1]; - return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), - std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, - color,opacity,pattern); - } - - template<typename tc> - CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, - const tc *const color, const float opacity, - const unsigned int pattern) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_ellipse(): Specified color is (null).", - cimg_instance); - if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity); - if (r1==r2 && (float)(int)r1==r1) { - if (pattern) return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity,pattern); - else return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity); - } - cimg_init_scanline(color,opacity); - const float - nr1 = cimg::abs(r1) - 0.5, nr2 = cimg::abs(r2) - 0.5, - nangle = (float)(angle*cimg::PI/180), - u = (float)std::cos(nangle), - v = (float)std::sin(nangle), - rmax = std::max(nr1,nr2), - l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2), - l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2), - a = l1*u*u + l2*v*v, - b = u*v*(l1 - l2), - c = l1*v*v + l2*u*u; - const int - yb = (int)cimg::round(std::sqrt(a*rmax*rmax/(a*c - b*b))), - tymin = y0 - yb - 1, - tymax = y0 + yb + 1, - ymin = tymin<0?0:tymin, - ymax = tymax>=height()?height() - 1:tymax; - int oxmin = 0, oxmax = 0; - bool first_line = true; - for (int y = ymin; y<=ymax; ++y) { - const float - Y = y - y0 + (y<y0?0.5f:-0.5f), - delta = b*b*Y*Y - a*(c*Y*Y - rmax*rmax), - sdelta = delta>0?(float)std::sqrt(delta)/a:0.f, - bY = b*Y/a, - fxmin = x0 - 0.5f - bY - sdelta, - fxmax = x0 + 0.5f - bY + sdelta; - const int xmin = (int)cimg::round(fxmin), xmax = (int)cimg::round(fxmax); - if (!pattern) cimg_draw_scanline(xmin,xmax,y,color,opacity,1); - else { - if (first_line) { - if (y0 - yb>=0) cimg_draw_scanline(xmin,xmax,y,color,opacity,1); - else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity); - first_line = false; - } else { - if (xmin<oxmin) cimg_draw_scanline(xmin,oxmin - 1,y,color,opacity,1); - else cimg_draw_scanline(oxmin + (oxmin==xmin?0:1),xmin,y,color,opacity,1); - if (xmax<oxmax) cimg_draw_scanline(xmax,oxmax - 1,y,color,opacity,1); - else cimg_draw_scanline(oxmax + (oxmax==xmax?0:1),xmax,y,color,opacity,1); - if (y==tymax) cimg_draw_scanline(xmin + 1,xmax - 1,y,color,opacity,1); - } - } - oxmin = xmin; oxmax = xmax; - } - return *this; - } - - //! Draw a filled 2D circle. - /** - \param x0 X-coordinate of the circle center. - \param y0 Y-coordinate of the circle center. - \param radius Circle radius. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \note - - Circle version of the Bresenham's algorithm is used. - **/ - template<typename tc> - CImg<T>& draw_circle(const int x0, const int y0, int radius, - const tc *const color, const float opacity=1) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_circle(): Specified color is (null).", - cimg_instance); - cimg_init_scanline(color,opacity); - if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; - if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1); - for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) { - if (f>=0) { - const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y; - if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1); - if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1); - f+=(ddFy+=2); --y; - } - const bool no_diag = y!=(x++); - ++(f+=(ddFx+=2)); - const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x; - if (no_diag) { - if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1); - if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1); - } - } - return *this; - } - - //! Draw an outlined 2D circle. - /** - \param x0 X-coordinate of the circle center. - \param y0 Y-coordinate of the circle center. - \param radius Circle radius. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \param pattern An integer whose bits describe the outline pattern. - **/ - template<typename tc> - CImg<T>& draw_circle(const int x0, const int y0, int radius, - const tc *const color, const float opacity, - const unsigned int pattern) { - cimg::unused(pattern); - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_circle(): Specified color is (null).", - cimg_instance); - if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; - if (!radius) return draw_point(x0,y0,color,opacity); - draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity). - draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity); - if (radius==1) return *this; - for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) { - if (f>=0) { f+=(ddFy+=2); --y; } - ++x; ++(f+=(ddFx+=2)); - if (x!=y + 1) { - const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x, - x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y; - draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). - draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); - if (x!=y) - draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). - draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); - } - } - return *this; - } - - //! Draw an image. - /** - \param sprite Sprite image. - \param x0 X-coordinate of the sprite position. - \param y0 Y-coordinate of the sprite position. - \param z0 Z-coordinate of the sprite position. - \param c0 C-coordinate of the sprite position. - \param opacity Drawing opacity. - **/ - template<typename t> - CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0, - const CImg<t>& sprite, const float opacity=1) { - if (is_empty() || !sprite) return *this; - if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); - if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) - return assign(sprite,false); - const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0); - const int - lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0), - lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0), - lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0), - lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0); - const t - *ptrs = sprite._data + - (bx?-x0:0) + - (by?-y0*(ulongT)sprite.width():0) + - (bz?-z0*(ulongT)sprite.width()*sprite.height():0) + - (bc?-c0*(ulongT)sprite.width()*sprite.height()*sprite.depth():0); - const ulongT - offX = (ulongT)_width - lX, - soffX = (ulongT)sprite._width - lX, - offY = (ulongT)_width*(_height - lY), - soffY = (ulongT)sprite._width*(sprite._height - lY), - offZ = (ulongT)_width*_height*(_depth - lZ), - soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - if (lX>0 && lY>0 && lZ>0 && lC>0) { - T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0); - for (int v = 0; v<lC; ++v) { - for (int z = 0; z<lZ; ++z) { - for (int y = 0; y<lY; ++y) { - if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++); - else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; } - ptrd+=offX; ptrs+=soffX; - } - ptrd+=offY; ptrs+=soffY; - } - ptrd+=offZ; ptrs+=soffZ; - } - } - return *this; - } - - //! Draw an image \specialization. - CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0, - const CImg<T>& sprite, const float opacity=1) { - if (is_empty() || !sprite) return *this; - if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); - if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) - return assign(sprite,false); - const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0); - const int - lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0), - lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0), - lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0), - lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0); - const T - *ptrs = sprite._data + - (bx?-x0:0) + - (by?-y0*(ulongT)sprite.width():0) + - (bz?-z0*(ulongT)sprite.width()*sprite.height():0) + - (bc?-c0*(ulongT)sprite.width()*sprite.height()*sprite.depth():0); - const ulongT - offX = (ulongT)_width - lX, - soffX = (ulongT)sprite._width - lX, - offY = (ulongT)_width*(_height - lY), - soffY = (ulongT)sprite._width*(sprite._height - lY), - offZ = (ulongT)_width*_height*(_depth - lZ), - soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ), - slX = lX*sizeof(T); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - if (lX>0 && lY>0 && lZ>0 && lC>0) { - T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0); - for (int v = 0; v<lC; ++v) { - for (int z = 0; z<lZ; ++z) { - if (opacity>=1) - for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; } - else for (int y = 0; y<lY; ++y) { - for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; } - ptrd+=offX; ptrs+=soffX; - } - ptrd+=offY; ptrs+=soffY; - } - ptrd+=offZ; ptrs+=soffZ; - } - } - return *this; - } - - //! Draw an image \overloading. - template<typename t> - CImg<T>& draw_image(const int x0, const int y0, const int z0, - const CImg<t>& sprite, const float opacity=1) { - return draw_image(x0,y0,z0,0,sprite,opacity); - } - - //! Draw an image \overloading. - template<typename t> - CImg<T>& draw_image(const int x0, const int y0, - const CImg<t>& sprite, const float opacity=1) { - return draw_image(x0,y0,0,sprite,opacity); - } - - //! Draw an image \overloading. - template<typename t> - CImg<T>& draw_image(const int x0, - const CImg<t>& sprite, const float opacity=1) { - return draw_image(x0,0,sprite,opacity); - } - - //! Draw an image \overloading. - template<typename t> - CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) { - return draw_image(0,sprite,opacity); - } - - //! Draw a masked image. - /** - \param sprite Sprite image. - \param mask Mask image. - \param x0 X-coordinate of the sprite position in the image instance. - \param y0 Y-coordinate of the sprite position in the image instance. - \param z0 Z-coordinate of the sprite position in the image instance. - \param c0 C-coordinate of the sprite position in the image instance. - \param mask_max_value Maximum pixel value of the mask image \c mask. - \param opacity Drawing opacity. - \note - - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite. - - Dimensions along x,y and z of \p sprite and \p mask must be the same. - **/ - template<typename ti, typename tm> - CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0, - const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1, - const float mask_max_value=1) { - if (is_empty() || !sprite || !mask) return *this; - if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value); - if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value); - if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth) - throw CImgArgumentException(_cimg_instance - "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have " - "incompatible dimensions.", - cimg_instance, - sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data, - mask._width,mask._height,mask._depth,mask._spectrum,mask._data); - - const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0); - const int - lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0), - lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0), - lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0), - lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0); - const ulongT - coff = (bx?-x0:0) + - (by?-y0*(ulongT)mask.width():0) + - (bz?-z0*(ulongT)mask.width()*mask.height():0) + - (bc?-c0*(ulongT)mask.width()*mask.height()*mask.depth():0), - ssize = (ulongT)mask.width()*mask.height()*mask.depth()*mask.spectrum(); - const ti *ptrs = sprite._data + coff; - const tm *ptrm = mask._data + coff; - const ulongT - offX = (ulongT)_width - lX, - soffX = (ulongT)sprite._width - lX, - offY = (ulongT)_width*(_height - lY), - soffY = (ulongT)sprite._width*(sprite._height - lY), - offZ = (ulongT)_width*_height*(_depth - lZ), - soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ); - if (lX>0 && lY>0 && lZ>0 && lC>0) { - T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0); - for (int c = 0; c<lC; ++c) { - ptrm = mask._data + (ptrm - mask._data)%ssize; - for (int z = 0; z<lZ; ++z) { - for (int y = 0; y<lY; ++y) { - for (int x = 0; x<lX; ++x) { - const float mopacity = (float)(*(ptrm++)*opacity), - nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.f); - *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value); - ++ptrd; - } - ptrd+=offX; ptrs+=soffX; ptrm+=soffX; - } - ptrd+=offY; ptrs+=soffY; ptrm+=soffY; - } - ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ; - } - } - return *this; - } - - //! Draw a masked image \overloading. - template<typename ti, typename tm> - CImg<T>& draw_image(const int x0, const int y0, const int z0, - const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1, - const float mask_max_value=1) { - return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value); - } - - //! Draw a image \overloading. - template<typename ti, typename tm> - CImg<T>& draw_image(const int x0, const int y0, - const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1, - const float mask_max_value=1) { - return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value); - } - - //! Draw a image \overloading. - template<typename ti, typename tm> - CImg<T>& draw_image(const int x0, - const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1, - const float mask_max_value=1) { - return draw_image(x0,0,sprite,mask,opacity,mask_max_value); - } - - //! Draw an image. - template<typename ti, typename tm> - CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1, - const float mask_max_value=1) { - return draw_image(0,sprite,mask,opacity,mask_max_value); - } - - //! Draw a text string. - /** - \param x0 X-coordinate of the text in the image instance. - \param y0 Y-coordinate of the text in the image instance. - \param text Format of the text ('printf'-style format string). - \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color. - \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color. - \param opacity Drawing opacity. - \param font Font used for drawing text. - **/ - template<typename tc1, typename tc2, typename t> - CImg<T>& draw_text(const int x0, const int y0, - const char *const text, - const tc1 *const foreground_color, const tc2 *const background_color, - const float opacity, const CImgList<t>& font, ...) { - if (!font) return *this; - CImg<charT> tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false); - } - - //! Draw a text string \overloading. - /** - \note A transparent background is used for the text. - **/ - template<typename tc, typename t> - CImg<T>& draw_text(const int x0, const int y0, - const char *const text, - const tc *const foreground_color, const int, - const float opacity, const CImgList<t>& font, ...) { - if (!font) return *this; - CImg<charT> tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false); - } - - //! Draw a text string \overloading. - /** - \note A transparent foreground is used for the text. - **/ - template<typename tc, typename t> - CImg<T>& draw_text(const int x0, const int y0, - const char *const text, - const int, const tc *const background_color, - const float opacity, const CImgList<t>& font, ...) { - if (!font) return *this; - CImg<charT> tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false); - } - - //! Draw a text string \overloading. - /** - \param x0 X-coordinate of the text in the image instance. - \param y0 Y-coordinate of the text in the image instance. - \param text Format of the text ('printf'-style format string). - \param foreground_color Array of spectrum() values of type \c T, - defining the foreground color (0 means 'transparent'). - \param background_color Array of spectrum() values of type \c T, - defining the background color (0 means 'transparent'). - \param opacity Drawing opacity. - \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise). - **/ - template<typename tc1, typename tc2> - CImg<T>& draw_text(const int x0, const int y0, - const char *const text, - const tc1 *const foreground_color, const tc2 *const background_color, - const float opacity=1, const unsigned int font_height=13, ...) { - if (!font_height) return *this; - CImg<charT> tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true); - _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true); - return *this; - } - - //! Draw a text string \overloading. - template<typename tc> - CImg<T>& draw_text(const int x0, const int y0, - const char *const text, - const tc *const foreground_color, const int background_color=0, - const float opacity=1, const unsigned int font_height=13, ...) { - if (!font_height) return *this; - cimg::unused(background_color); - CImg<charT> tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data); - } - - //! Draw a text string \overloading. - template<typename tc> - CImg<T>& draw_text(const int x0, const int y0, - const char *const text, - const int, const tc *const background_color, - const float opacity=1, const unsigned int font_height=13, ...) { - if (!font_height) return *this; - CImg<charT> tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data); - } - - template<typename tc1, typename tc2, typename t> - CImg<T>& _draw_text(const int x0, const int y0, - const char *const text, - const tc1 *const foreground_color, const tc2 *const background_color, - const float opacity, const CImgList<t>& font, - const bool is_native_font) { - if (!text) return *this; - if (!font) - throw CImgArgumentException(_cimg_instance - "draw_text(): Empty specified font.", - cimg_instance); - - const unsigned int text_length = (unsigned int)std::strlen(text); - if (is_empty()) { - // If needed, pre-compute necessary size of the image - int x = 0, y = 0, w = 0; - unsigned char c = 0; - for (unsigned int i = 0; i<text_length; ++i) { - c = (unsigned char)text[i]; - switch (c) { - case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break; - case '\t' : x+=4*font[' ']._width; break; - default : if (c<font._width) x+=font[c]._width; - } - } - if (x!=0 || c=='\n') { - if (x>w) w=x; - y+=font[0]._height; - } - assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0); - } - - int x = x0, y = y0; - for (unsigned int i = 0; i<text_length; ++i) { - const unsigned char c = (unsigned char)text[i]; - switch (c) { - case '\n' : y+=font[0]._height; x = x0; break; - case '\t' : x+=4*font[' ']._width; break; - default : if (c<font._width) { - CImg<T> letter = font[c]; - if (letter) { - if (is_native_font && _spectrum>letter._spectrum) letter.resize(-100,-100,1,_spectrum,0,2); - const unsigned int cmin = std::min(_spectrum,letter._spectrum); - if (foreground_color) - for (unsigned int c = 0; c<cmin; ++c) - if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c]; - if (c + 256<font.width()) { // Letter has mask - if (background_color) - for (unsigned int c = 0; c<cmin; ++c) - draw_rectangle(x,y,0,c,x + letter._width - 1,y + letter._height - 1,0,c, - background_color[c],opacity); - draw_image(x,y,letter,font[c + 256],opacity,255.f); - } else draw_image(x,y,letter,opacity); // Letter has no mask - x+=letter._width; - } - } - } - } - return *this; - } - - // [internal] Version used to display text in interactive viewers. - CImg<T>& __draw_text(const char *const text, const bool is_down, ...) { - CImg<charT> tmp(2048); - std::va_list ap; va_start(ap,is_down); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - CImg<ucharT> label, labelmask; - const unsigned char labelcolor = 127; - const unsigned int fsize = 13; - label.draw_text(0,0,"%s",&labelcolor,0,1,fsize,tmp._data); - if (label) { - label.crop(2,0,label.width() - 1,label.height()); - ((labelmask = label)+=label.get_dilate(5)).max(80); - (label*=2).resize(-100,-100,1,3,1); - return draw_image(0,is_down?height() - fsize:0,label,labelmask,1,254); - } - return *this; - } - - //! Draw a 2D vector field. - /** - \param flow Image of 2D vectors used as input data. - \param color Image of spectrum()-D vectors corresponding to the color of each arrow. - \param opacity Drawing opacity. - \param sampling Length (in pixels) between each arrow. - \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). - \param is_arrow Tells if arrows must be drawn, instead of oriented segments. - \param pattern Used pattern to draw lines. - \note Clipping is supported. - **/ - template<typename t1, typename t2> - CImg<T>& draw_quiver(const CImg<t1>& flow, - const t2 *const color, const float opacity=1, - const unsigned int sampling=25, const float factor=-20, - const bool is_arrow=true, const unsigned int pattern=~0U) { - return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern); - } - - //! Draw a 2D vector field, using a field of colors. - /** - \param flow Image of 2D vectors used as input data. - \param color Image of spectrum()-D vectors corresponding to the color of each arrow. - \param opacity Opacity of the drawing. - \param sampling Length (in pixels) between each arrow. - \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). - \param is_arrow Tells if arrows must be drawn, instead of oriented segments. - \param pattern Used pattern to draw lines. - \note Clipping is supported. - **/ - template<typename t1, typename t2> - CImg<T>& draw_quiver(const CImg<t1>& flow, - const CImg<t2>& color, const float opacity=1, - const unsigned int sampling=25, const float factor=-20, - const bool is_arrow=true, const unsigned int pattern=~0U) { - if (is_empty()) return *this; - if (!flow || flow._spectrum!=2) - throw CImgArgumentException(_cimg_instance - "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).", - cimg_instance, - flow._width,flow._height,flow._depth,flow._spectrum,flow._data); - if (sampling<=0) - throw CImgArgumentException(_cimg_instance - "draw_quiver(): Invalid sampling value %g " - "(should be >0)", - cimg_instance, - sampling); - const bool colorfield = (color._width==flow._width && color._height==flow._height && - color._depth==1 && color._spectrum==_spectrum); - if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern); - float vmax,fact; - if (factor<=0) { - float m, M = (float)flow.get_norm(2).max_min(m); - vmax = (float)std::max(cimg::abs(m),cimg::abs(M)); - if (!vmax) vmax = 1; - fact = -factor; - } else { fact = factor; vmax = 1; } - - for (unsigned int y = sampling/2; y<_height; y+=sampling) - for (unsigned int x = sampling/2; x<_width; x+=sampling) { - const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height; - float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax; - if (is_arrow) { - const int xx = (int)(x + u), yy = (int)(y + v); - if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern); - else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern); - } else { - if (colorfield) - draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), - color.get_vector_at(X,Y)._data,opacity,pattern); - else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), - color._data,opacity,pattern); - } - } - return *this; - } - - //! Draw a labeled horizontal axis. - /** - \param values_x Values along the horizontal axis. - \param y Y-coordinate of the horizontal axis in the image instance. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \param pattern Drawing pattern. - \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). - \param allow_zero Enable/disable the drawing of label '0' if found. - **/ - template<typename t, typename tc> - CImg<T>& draw_axis(const CImg<t>& values_x, const int y, - const tc *const color, const float opacity=1, - const unsigned int pattern=~0U, const unsigned int font_height=13, - const bool allow_zero=true) { - if (is_empty()) return *this; - const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height; - const int siz = (int)values_x.size() - 1; - CImg<charT> txt(32); - CImg<T> label; - if (siz<=0) { // Degenerated case - draw_line(0,y,_width - 1,y,color,opacity,pattern); - if (!siz) { - cimg_snprintf(txt,txt._width,"%g",(double)*values_x); - label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); - const int - _xt = (width() - label.width())/2, - xt = _xt<3?3:_xt + label.width()>=width() - 2?width() - 3 - label.width():_xt; - draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity); - if (allow_zero || *txt!='0' || txt[1]!=0) - draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); - } - } else { // Regular case - if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern); - else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern); - cimg_foroff(values_x,x) { - cimg_snprintf(txt,txt._width,"%g",(double)values_x(x)); - label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); - const int - xi = (int)(x*(_width - 1)/siz), - _xt = xi - label.width()/2, - xt = _xt<3?3:_xt + label.width()>=width() - 2?width() - 3 - label.width():_xt; - draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity); - if (allow_zero || *txt!='0' || txt[1]!=0) - draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); - } - } - return *this; - } - - //! Draw a labeled vertical axis. - /** - \param x X-coordinate of the vertical axis in the image instance. - \param values_y Values along the Y-axis. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \param pattern Drawing pattern. - \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). - \param allow_zero Enable/disable the drawing of label '0' if found. - **/ - template<typename t, typename tc> - CImg<T>& draw_axis(const int x, const CImg<t>& values_y, - const tc *const color, const float opacity=1, - const unsigned int pattern=~0U, const unsigned int font_height=13, - const bool allow_zero=true) { - if (is_empty()) return *this; - int siz = (int)values_y.size() - 1; - CImg<charT> txt(32); - CImg<T> label; - if (siz<=0) { // Degenerated case - draw_line(x,0,x,_height - 1,color,opacity,pattern); - if (!siz) { - cimg_snprintf(txt,txt._width,"%g",(double)*values_y); - label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); - const int - _yt = (height() - label.height())/2, - yt = _yt<0?0:_yt + label.height()>=height()?height() - 1-label.height():_yt, - _xt = x - 2 - label.width(), - xt = _xt>=0?_xt:x + 3; - draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity); - if (allow_zero || *txt!='0' || txt[1]!=0) - draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); - } - } else { // Regular case - if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern); - else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern); - cimg_foroff(values_y,y) { - cimg_snprintf(txt,txt._width,"%g",(double)values_y(y)); - label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); - const int - yi = (int)(y*(_height - 1)/siz), - _yt = yi - label.height()/2, - yt = _yt<0?0:_yt + label.height()>=height()?height() - 1-label.height():_yt, - _xt = x - 2 - label.width(), - xt = _xt>=0?_xt:x + 3; - draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity); - if (allow_zero || *txt!='0' || txt[1]!=0) - draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); - } - } - return *this; - } - - //! Draw labeled horizontal and vertical axes. - /** - \param values_x Values along the X-axis. - \param values_y Values along the Y-axis. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \param pattern_x Drawing pattern for the X-axis. - \param pattern_y Drawing pattern for the Y-axis. - \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). - \param allow_zero Enable/disable the drawing of label '0' if found. - **/ - template<typename tx, typename ty, typename tc> - CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y, - const tc *const color, const float opacity=1, - const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, - const unsigned int font_height=13, const bool allow_zero=true) { - if (is_empty()) return *this; - const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true); - const int sizx = (int)values_x.size() - 1, wm1 = width() - 1; - if (sizx>=0) { - float ox = (float)*nvalues_x; - for (unsigned int x = sizx?1U:0U; x<_width; ++x) { - const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1); - if (nx*ox<=0) { draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero); break; } - ox = nx; - } - } - const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true); - const int sizy = (int)values_y.size() - 1, hm1 = height() - 1; - if (sizy>0) { - float oy = (float)nvalues_y[0]; - for (unsigned int y = sizy?1U:0U; y<_height; ++y) { - const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1); - if (ny*oy<=0) { draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero); break; } - oy = ny; - } - } - return *this; - } - - //! Draw labeled horizontal and vertical axes \overloading. - template<typename tc> - CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1, - const tc *const color, const float opacity=1, - const int subdivisionx=-60, const int subdivisiony=-60, - const float precisionx=0, const float precisiony=0, - const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, - const unsigned int font_height=13) { - if (is_empty()) return *this; - const bool allow_zero = (x0*x1>0) || (y0*y1>0); - const float - dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0), - px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx, - py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony; - if (x0!=x1 && y0!=y1) - draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px), - CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py), - color,opacity,pattern_x,pattern_y,font_height,allow_zero); - else if (x0==x1 && y0!=y1) - draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py), - color,opacity,pattern_y,font_height); - else if (x0!=x1 && y0==y1) - draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0, - color,opacity,pattern_x,font_height); - return *this; - } - - //! Draw 2D grid. - /** - \param values_x X-coordinates of the vertical lines. - \param values_y Y-coordinates of the horizontal lines. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - \param pattern_x Drawing pattern for vertical lines. - \param pattern_y Drawing pattern for horizontal lines. - **/ - template<typename tx, typename ty, typename tc> - CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y, - const tc *const color, const float opacity=1, - const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { - if (is_empty()) return *this; - if (values_x) cimg_foroff(values_x,x) { - const int xi = (int)values_x[x]; - if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x); - } - if (values_y) cimg_foroff(values_y,y) { - const int yi = (int)values_y[y]; - if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y); - } - return *this; - } - - //! Draw 2D grid \simplification. - template<typename tc> - CImg<T>& draw_grid(const float delta_x, const float delta_y, - const float offsetx, const float offsety, - const bool invertx, const bool inverty, - const tc *const color, const float opacity=1, - const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { - if (is_empty()) return *this; - CImg<uintT> seqx, seqy; - if (delta_x!=0) { - const float dx = delta_x>0?delta_x:_width*-delta_x/100; - const unsigned int nx = (unsigned int)(_width/dx); - seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx)); - if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width); - if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x); - } - if (delta_y!=0) { - const float dy = delta_y>0?delta_y:_height*-delta_y/100; - const unsigned int ny = (unsigned int)(_height/dy); - seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny)); - if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height); - if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y); - } - return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y); - } - - //! Draw 1D graph. - /** - \param data Image containing the graph values I = f(x). - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - - \param plot_type Define the type of the plot: - - 0 = No plot. - - 1 = Plot using segments. - - 2 = Plot using cubic splines. - - 3 = Plot with bars. - \param vertex_type Define the type of points: - - 0 = No points. - - 1 = Point. - - 2 = Straight cross. - - 3 = Diagonal cross. - - 4 = Filled circle. - - 5 = Outlined circle. - - 6 = Square. - - 7 = Diamond. - \param ymin Lower bound of the y-range. - \param ymax Upper bound of the y-range. - \param pattern Drawing pattern. - \note - - if \c ymin==ymax==0, the y-range is computed automatically from the input samples. - **/ - template<typename t, typename tc> - CImg<T>& draw_graph(const CImg<t>& data, - const tc *const color, const float opacity=1, - const unsigned int plot_type=1, const int vertex_type=1, - const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) { - if (is_empty() || _height<=1) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_graph(): Specified color is (null).", - cimg_instance); - - // Create shaded colors for displaying bar plots. - CImg<tc> color1, color2; - if (plot_type==3) { - color1.assign(_spectrum); color2.assign(_spectrum); - cimg_forC(*this,c) { - color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f); - color2[c] = (tc)(color[c]*0.4f); - } - } - - // Compute min/max and normalization factors. - const ulongT - siz = data.size(), - _siz1 = siz - (plot_type!=3), - siz1 = _siz1?_siz1:1; - const unsigned int - _width1 = _width - (plot_type!=3), - width1 = _width1?_width1:1; - double m = ymin, M = ymax; - if (ymin==ymax) m = (double)data.max_min(M); - if (m==M) { --m; ++M; } - const float ca = (float)(M-m)/(_height - 1); - bool init_hatch = true; - - // Draw graph edges - switch (plot_type%4) { - case 1 : { // Segments - int oX = 0, oY = (int)((data[0] - m)/ca); - if (siz==1) { - const int Y = (int)((*data - m)/ca); - draw_line(0,Y,width() - 1,Y,color,opacity,pattern); - } else { - const float fx = (float)_width/siz1; - for (ulongT off = 1; off<siz; ++off) { - const int - X = (int)(off*fx) - 1, - Y = (int)((data[off]-m)/ca); - draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch); - oX = X; oY = Y; - init_hatch = false; - } - } - } break; - case 2 : { // Spline - const CImg<t> ndata(data._data,siz,1,1,1,true); - int oY = (int)((data[0] - m)/ca); - cimg_forX(*this,x) { - const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-m)/ca); - if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch); - init_hatch = false; - oY = Y; - } - } break; - case 3 : { // Bars - const int Y0 = (int)(-m/ca); - const float fx = (float)_width/siz1; - int oX = 0; - cimg_foroff(data,off) { - const int - X = (int)((off + 1)*fx) - 1, - Y = (int)((data[off] - m)/ca); - draw_rectangle(oX,Y0,X,Y,color,opacity). - draw_line(oX,Y,oX,Y0,color2.data(),opacity). - draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity). - draw_line(X,Y,X,Y0,color1.data(),opacity). - draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity); - oX = X + 1; - } - } break; - default : break; // No edges - } - - // Draw graph points - const unsigned int wb2 = plot_type==3?_width1/(2*siz):0; - const float fx = (float)_width1/siz1; - switch (vertex_type%8) { - case 1 : { // Point - cimg_foroff(data,off) { - const int - X = (int)(off*fx + wb2), - Y = (int)((data[off]-m)/ca); - draw_point(X,Y,color,opacity); - } - } break; - case 2 : { // Straight Cross - cimg_foroff(data,off) { - const int - X = (int)(off*fx + wb2), - Y = (int)((data[off]-m)/ca); - draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity); - } - } break; - case 3 : { // Diagonal Cross - cimg_foroff(data,off) { - const int - X = (int)(off*fx + wb2), - Y = (int)((data[off]-m)/ca); - draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity); - } - } break; - case 4 : { // Filled Circle - cimg_foroff(data,off) { - const int - X = (int)(off*fx + wb2), - Y = (int)((data[off]-m)/ca); - draw_circle(X,Y,3,color,opacity); - } - } break; - case 5 : { // Outlined circle - cimg_foroff(data,off) { - const int - X = (int)(off*fx + wb2), - Y = (int)((data[off]-m)/ca); - draw_circle(X,Y,3,color,opacity,0U); - } - } break; - case 6 : { // Square - cimg_foroff(data,off) { - const int - X = (int)(off*fx + wb2), - Y = (int)((data[off]-m)/ca); - draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U); - } - } break; - case 7 : { // Diamond - cimg_foroff(data,off) { - const int - X = (int)(off*fx + wb2), - Y = (int)((data[off]-m)/ca); - draw_line(X,Y - 4,X + 4,Y,color,opacity). - draw_line(X + 4,Y,X,Y + 4,color,opacity). - draw_line(X,Y + 4,X - 4,Y,color,opacity). - draw_line(X - 4,Y,X,Y - 4,color,opacity); - } - } break; - default : break; // No points - } - return *this; - } - - bool _draw_fill(const int x, const int y, const int z, - const CImg<T>& ref, const float tolerance2) const { - const T *ptr1 = data(x,y,z), *ptr2 = ref._data; - const unsigned long off = _width*_height*_depth; - float diff = 0; - cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; } - return diff<=tolerance2; - } - - //! Draw filled 3D region with the flood fill algorithm. - /** - \param x0 X-coordinate of the starting point of the region to fill. - \param y0 Y-coordinate of the starting point of the region to fill. - \param z0 Z-coordinate of the starting point of the region to fill. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param[out] region Image that will contain the mask of the filled region mask, as an output. - \param tolerance Tolerance concerning neighborhood values. - \param opacity Opacity of the drawing. - \param is_high_connectivity Tells if 8-connexity must be used. - \return \c region is initialized with the binary mask of the filled region. - **/ - template<typename tc, typename t> - CImg<T>& draw_fill(const int x0, const int y0, const int z0, - const tc *const color, const float opacity, - CImg<t> ®ion, - const float tolerance = 0, - const bool is_high_connectivity = false) { -#define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \ - stack[N] = x; stack(N,1) = y; stack(N++,2) = z -#define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2) -#define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2) - - if (!containsXYZC(x0,y0,z0,0)) return *this; - const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f); - const float tolerance2 = cimg::sqr(tolerance); - const CImg<T> ref = get_vector_at(x0,y0,z0); - CImg<uintT> stack(256,1,1,3); - CImg<ucharT> _region(_width,_height,_depth,1,0); - unsigned int N = 0; - int x, y, z; - - _draw_fill_push(x0,y0,z0); - while (N>0) { - _draw_fill_pop(x,y,z); - if (!_region(x,y,z)) { - const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1; - int xl = x, xr = x; - - // Using these booleans reduces the number of pushes drastically. - bool is_yp = false, is_yn = false, is_zp = false, is_zn = false; - for (int step = -1; step<2; step+=2) { - while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) { - if (yp>=0 && _draw_fill_is_inside(x,yp,z)) { - if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; } - } else is_yp = false; - if (yn<height() && _draw_fill_is_inside(x,yn,z)) { - if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; } - } else is_yn = false; - if (depth()>1) { - if (zp>=0 && _draw_fill_is_inside(x,y,zp)) { - if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; } - } else is_zp = false; - if (zn<depth() && _draw_fill_is_inside(x,y,zn)) { - if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; } - } else is_zn = false; - } - if (is_high_connectivity) { - const int xp = x - 1, xn = x + 1; - if (yp>=0 && !is_yp) { - if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) { - _draw_fill_push(xp,yp,z); if (step<0) is_yp = true; - } - if (xn<width() && _draw_fill_is_inside(xn,yp,z)) { - _draw_fill_push(xn,yp,z); if (step>0) is_yp = true; - } - } - if (yn<height() && !is_yn) { - if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) { - _draw_fill_push(xp,yn,z); if (step<0) is_yn = true; - } - if (xn<width() && _draw_fill_is_inside(xn,yn,z)) { - _draw_fill_push(xn,yn,z); if (step>0) is_yn = true; - } - } - if (depth()>1) { - if (zp>=0 && !is_zp) { - if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) { - _draw_fill_push(xp,y,zp); if (step<0) is_zp = true; - } - if (xn<width() && _draw_fill_is_inside(xn,y,zp)) { - _draw_fill_push(xn,y,zp); if (step>0) is_zp = true; - } - - if (yp>=0 && !is_yp) { - if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); } - if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); } - if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); } - } - if (yn<height() && !is_yn) { - if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); } - if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); } - if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); } - } - } - - if (zn<depth() && !is_zn) { - if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) { - _draw_fill_push(xp,y,zn); if (step<0) is_zn = true; - } - if (xn<width() && _draw_fill_is_inside(xn,y,zn)) { - _draw_fill_push(xn,y,zn); if (step>0) is_zn = true; - } - - if (yp>=0 && !is_yp) { - if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); } - if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); } - if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); } - } - if (yn<height() && !is_yn) { - if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); } - if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); } - if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); } - } - } - } - } - x+=step; - } - if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; } - else xr = --x; - } - std::memset(_region.data(xl,y,z),1,xr - xl + 1); - if (opacity==1) { - if (sizeof(T)==1) { - const int dx = xr - xl + 1; - cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx); - } else cimg_forC(*this,c) { - const T val = (T)color[c]; - T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val; - } - } else cimg_forC(*this,c) { - const T val = (T)(color[c]*nopacity); - T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; } - } - } - } - _region.move_to(region); - return *this; - } - - //! Draw filled 3D region with the flood fill algorithm \simplification. - template<typename tc> - CImg<T>& draw_fill(const int x0, const int y0, const int z0, - const tc *const color, const float opacity=1, - const float tolerance=0, const bool is_high_connexity=false) { - CImg<ucharT> tmp; - return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity); - } - - //! Draw filled 2D region with the flood fill algorithm \simplification. - template<typename tc> - CImg<T>& draw_fill(const int x0, const int y0, - const tc *const color, const float opacity=1, - const float tolerance=0, const bool is_high_connexity=false) { - CImg<ucharT> tmp; - return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity); - } - - //! Draw a random plasma texture. - /** - \param alpha Alpha-parameter. - \param beta Beta-parameter. - \param scale Scale-parameter. - \note Use the mid-point algorithm to render. - **/ - CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) { - if (is_empty()) return *this; - const int w = width(), h = height(); - const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max(); - ulongT rng = (cimg::_rand(),cimg::rng()); - cimg_forZC(*this,z,c) { - CImg<T> ref = get_shared_slice(z,c); - for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) { - const int delta2 = delta>>1; - const float r = alpha*delta + beta; - - // Square step. - for (int y0 = 0; y0<h; y0+=delta) - for (int x0 = 0; x0<w; x0+=delta) { - const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h; - const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) + - r*cimg::rand(-1,1,&rng)); - ref(xc,yc) = (T)(val<m?m:val>M?M:val); - } - - // Diamond steps. - for (int y = -delta2; y<h; y+=delta) - for (int x0=0; x0<w; x0+=delta) { - const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h, - xc = (x0 + delta2)%w, yc = (y + delta2)%h; - const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + - r*cimg::rand(-1,1,&rng)); - ref(xc,yc) = (T)(val<m?m:val>M?M:val); - } - for (int y0 = 0; y0<h; y0+=delta) - for (int x = -delta2; x<w; x+=delta) { - const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h, - xc = (x + delta2)%w, yc = (y0 + delta2)%h; - const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + - r*cimg::rand(-1,1,&rng)); - ref(xc,yc) = (T)(val<m?m:val>M?M:val); - } - for (int y = -delta2; y<h; y+=delta) - for (int x = -delta2; x<w; x+=delta) { - const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h, - xc = (x + delta2)%w, yc = (y + delta2)%h; - const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + - r*cimg::rand(-1,1,&rng)); - ref(xc,yc) = (T)(val<m?m:val>M?M:val); - } - } - } - cimg::srand(rng); - return *this; - } - - //! Draw a quadratic Mandelbrot or Julia 2D fractal. - /** - \param x0 X-coordinate of the upper-left pixel. - \param y0 Y-coordinate of the upper-left pixel. - \param x1 X-coordinate of the lower-right pixel. - \param y1 Y-coordinate of the lower-right pixel. - \param colormap Colormap. - \param opacity Drawing opacity. - \param z0r Real part of the upper-left fractal vertex. - \param z0i Imaginary part of the upper-left fractal vertex. - \param z1r Real part of the lower-right fractal vertex. - \param z1i Imaginary part of the lower-right fractal vertex. - \param iteration_max Maximum number of iterations for each estimated point. - \param is_normalized_iteration Tells if iterations are normalized. - \param is_julia_set Tells if the Mandelbrot or Julia set is rendered. - \param param_r Real part of the Julia set parameter. - \param param_i Imaginary part of the Julia set parameter. - \note Fractal rendering is done by the Escape Time Algorithm. - **/ - template<typename tc> - CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, - const CImg<tc>& colormap, const float opacity=1, - const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, - const unsigned int iteration_max=255, - const bool is_normalized_iteration=false, - const bool is_julia_set=false, - const double param_r=0, const double param_i=0) { - if (is_empty()) return *this; - CImg<tc> palette; - if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true); - if (palette && palette._spectrum!=_spectrum) - throw CImgArgumentException(_cimg_instance - "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have " - "incompatible dimensions.", - cimg_instance, - colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); - - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.); - const int - _x0 = cimg::cut(x0,0,width() - 1), - _y0 = cimg::cut(y0,0,height() - 1), - _x1 = cimg::cut(x1,0,width() - 1), - _y1 = cimg::cut(y1,0,height() - 1); - - cimg_pragma_openmp(parallel for collapse(2) - cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048)) - for (int q = _y0; q<=_y1; ++q) - for (int p = _x0; p<=_x1; ++p) { - unsigned int iteration = 0; - const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height; - double zr, zi, cr, ci; - if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; } - else { zr = param_r; zi = param_i; cr = x; ci = y; } - for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) { - const double temp = zr*zr - zi*zi + cr; - zi = 2*zr*zi + ci; - zr = temp; - } - if (iteration>iteration_max) { - if (palette) { - if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c); - else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity); - } else { - if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0; - else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity); - } - } else if (is_normalized_iteration) { - const float - normz = (float)cimg::abs(zr*zr + zi*zi), - niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2); - if (palette) { - if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c); - else cimg_forC(*this,c) - (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity); - } else { - if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration; - else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity); - } - } else { - if (palette) { - if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c); - else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity); - } else { - if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration; - else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity); - } - } - } - return *this; - } - - //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading. - template<typename tc> - CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1, - const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, - const unsigned int iteration_max=255, - const bool is_normalized_iteration=false, - const bool is_julia_set=false, - const double param_r=0, const double param_i=0) { - return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity, - z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i); - } - - //! Draw a 1D gaussian function. - /** - \param xc X-coordinate of the gaussian center. - \param sigma Standard variation of the gaussian distribution. - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename tc> - CImg<T>& draw_gaussian(const float xc, const float sigma, - const tc *const color, const float opacity=1) { - if (is_empty()) return *this; - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_gaussian(): Specified color is (null).", - cimg_instance); - const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT whd = (ulongT)_width*_height*_depth; - const tc *col = color; - cimg_forX(*this,x) { - const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2); - T *ptrd = data(x,0,0,0); - if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } - else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } - col-=_spectrum; - } - return *this; - } - - //! Draw a 2D gaussian function. - /** - \param xc X-coordinate of the gaussian center. - \param yc Y-coordinate of the gaussian center. - \param tensor Covariance matrix (must be 2x2). - \param color Pointer to \c spectrum() consecutive values, defining the drawing color. - \param opacity Drawing opacity. - **/ - template<typename t, typename tc> - CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor, - const tc *const color, const float opacity=1) { - if (is_empty()) return *this; - if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1) - throw CImgArgumentException(_cimg_instance - "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.", - cimg_instance, - tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_gaussian(): Specified color is (null).", - cimg_instance); - typedef typename CImg<t>::Tfloat tfloat; - const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; - const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT whd = (ulongT)_width*_height*_depth; - const tc *col = color; - float dy = -yc; - cimg_forY(*this,y) { - float dx = -xc; - cimg_forX(*this,x) { - const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy); - T *ptrd = data(x,y,0,0); - if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } - else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } - col-=_spectrum; - ++dx; - } - ++dy; - } - return *this; - } - - //! Draw a 2D gaussian function \overloading. - template<typename tc> - CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, - const tc *const color, const float opacity=1) { - const double - a = r1*ru*ru + r2*rv*rv, - b = (r1-r2)*ru*rv, - c = r1*rv*rv + r2*ru*ru; - const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c); - return draw_gaussian(xc,yc,tensor,color,opacity); - } - - //! Draw a 2D gaussian function \overloading. - template<typename tc> - CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma, - const tc *const color, const float opacity=1) { - return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity); - } - - //! Draw a 3D gaussian function \overloading. - template<typename t, typename tc> - CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor, - const tc *const color, const float opacity=1) { - if (is_empty()) return *this; - typedef typename CImg<t>::Tfloat tfloat; - if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1) - throw CImgArgumentException(_cimg_instance - "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.", - cimg_instance, - tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); - - const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; - const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2); - const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); - const ulongT whd = (ulongT)_width*_height*_depth; - const tc *col = color; - cimg_forXYZ(*this,x,y,z) { - const float - dx = (x - xc), dy = (y - yc), dz = (z - zc), - val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); - T *ptrd = data(x,y,z,0); - if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } - else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } - col-=_spectrum; - } - return *this; - } - - //! Draw a 3D gaussian function \overloading. - template<typename tc> - CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, - const tc *const color, const float opacity=1) { - return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity); - } - - //! Draw a 3D object. - /** - \param x0 X-coordinate of the 3D object position - \param y0 Y-coordinate of the 3D object position - \param z0 Z-coordinate of the 3D object position - \param vertices Image Nx3 describing 3D point coordinates - \param primitives List of P primitives - \param colors List of P color (or textures) - \param opacities Image or list of P opacities - \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) - \param is_double_sided Tells if object faces have two sides or are oriented. - \param focale length of the focale (0 for parallel projection) - \param lightx X-coordinate of the light - \param lighty Y-coordinate of the light - \param lightz Z-coordinate of the light - \param specular_lightness Amount of specular light. - \param specular_shininess Shininess of the object - \param g_opacity Global opacity of the object. - **/ - template<typename tp, typename tf, typename tc, typename to> - CImg<T>& draw_object3d(const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImg<to>& opacities, - const unsigned int render_type=4, - const bool is_double_sided=false, const float focale=700, - const float lightx=0, const float lighty=0, const float lightz=-5e8, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const float g_opacity=1) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, - is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty()); - } - - //! Draw a 3D object \simplification. - template<typename tp, typename tf, typename tc, typename to, typename tz> - CImg<T>& draw_object3d(const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImg<to>& opacities, - const unsigned int render_type, - const bool is_double_sided, const float focale, - const float lightx, const float lighty, const float lightz, - const float specular_lightness, const float specular_shininess, - const float g_opacity, CImg<tz>& zbuffer) { - return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,1); - } - -#ifdef cimg_use_board - template<typename tp, typename tf, typename tc, typename to> - CImg<T>& draw_object3d(LibBoard::Board& board, - const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImg<to>& opacities, - const unsigned int render_type=4, - const bool is_double_sided=false, const float focale=700, - const float lightx=0, const float lighty=0, const float lightz=-5e8, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const float g_opacity=1) { - return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, - is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty()); - } - - template<typename tp, typename tf, typename tc, typename to, typename tz> - CImg<T>& draw_object3d(LibBoard::Board& board, - const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImg<to>& opacities, - const unsigned int render_type, - const bool is_double_sided, const float focale, - const float lightx, const float lighty, const float lightz, - const float specular_lightness, const float specular_shininess, - const float g_opacity, CImg<tz>& zbuffer) { - return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,1); - } -#endif - - //! Draw a 3D object \simplification. - template<typename tp, typename tf, typename tc, typename to> - CImg<T>& draw_object3d(const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImgList<to>& opacities, - const unsigned int render_type=4, - const bool is_double_sided=false, const float focale=700, - const float lightx=0, const float lighty=0, const float lightz=-5e8, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const float g_opacity=1) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, - is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty()); - } - - //! Draw a 3D object \simplification. - template<typename tp, typename tf, typename tc, typename to, typename tz> - CImg<T>& draw_object3d(const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImgList<to>& opacities, - const unsigned int render_type, - const bool is_double_sided, const float focale, - const float lightx, const float lighty, const float lightz, - const float specular_lightness, const float specular_shininess, - const float g_opacity, CImg<tz>& zbuffer) { - return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,1); - } - -#ifdef cimg_use_board - template<typename tp, typename tf, typename tc, typename to> - CImg<T>& draw_object3d(LibBoard::Board& board, - const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImgList<to>& opacities, - const unsigned int render_type=4, - const bool is_double_sided=false, const float focale=700, - const float lightx=0, const float lighty=0, const float lightz=-5e8, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const float g_opacity=1) { - return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, - is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty()); - } - - template<typename tp, typename tf, typename tc, typename to, typename tz> - CImg<T>& draw_object3d(LibBoard::Board& board, - const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, const CImgList<to>& opacities, - const unsigned int render_type, - const bool is_double_sided, const float focale, - const float lightx, const float lighty, const float lightz, - const float specular_lightness, const float specular_shininess, - const float g_opacity, CImg<tz>& zbuffer) { - return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,1); - } -#endif - - //! Draw a 3D object \simplification. - template<typename tp, typename tf, typename tc> - CImg<T>& draw_object3d(const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const unsigned int render_type=4, - const bool is_double_sided=false, const float focale=700, - const float lightx=0, const float lighty=0, const float lightz=-5e8, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const float g_opacity=1) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(), - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty()); - } - - //! Draw a 3D object \simplification. - template<typename tp, typename tf, typename tc, typename tz> - CImg<T>& draw_object3d(const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const unsigned int render_type, - const bool is_double_sided, const float focale, - const float lightx, const float lighty, const float lightz, - const float specular_lightness, const float specular_shininess, - const float g_opacity, CImg<tz>& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(), - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,zbuffer); - } - -#ifdef cimg_use_board - template<typename tp, typename tf, typename tc, typename to> - CImg<T>& draw_object3d(LibBoard::Board& board, - const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const unsigned int render_type=4, - const bool is_double_sided=false, const float focale=700, - const float lightx=0, const float lighty=0, const float lightz=-5e8, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const float g_opacity=1) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(), - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty()); - } - - template<typename tp, typename tf, typename tc, typename to, typename tz> - CImg<T>& draw_object3d(LibBoard::Board& board, - const float x0, const float y0, const float z0, - const CImg<tp>& vertices, const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const unsigned int render_type, - const bool is_double_sided, const float focale, - const float lightx, const float lighty, const float lightz, - const float specular_lightness, const float specular_shininess, - const float g_opacity, CImg<tz>& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(), - render_type,is_double_sided,focale,lightx,lighty,lightz, - specular_lightness,specular_shininess,g_opacity,zbuffer); - } -#endif - - template<typename t, typename to> - static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) { - if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; } - if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); } - opacity.assign(opacities[n_primitive],true); - return 1.f; - } - - template<typename t, typename to> - static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) { - opacity.assign(); - return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive]; - } - - template<typename t> - static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) { - return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.f; - } - - template<typename t> - static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) { - return n_primitive<opacities._width?(float)opacities[n_primitive]:1.f; - } - - template<typename tz, typename tp, typename tf, typename tc, typename to> - CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer, - const float X, const float Y, const float Z, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const to& opacities, - const unsigned int render_type, - const bool is_double_sided, const float focale, - const float lightx, const float lighty, const float lightz, - const float specular_lightness, const float specular_shininess, - const float g_opacity, const float sprite_scale) { - typedef typename cimg::superset2<tp,tz,float>::type tpfloat; - typedef typename to::value_type _to; - if (is_empty() || !vertices || !primitives) return *this; - CImg<char> error_message(1024); - if (!vertices.is_object3d(primitives,colors,opacities,false,error_message)) - throw CImgArgumentException(_cimg_instance - "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).", - cimg_instance,vertices._width,primitives._width,error_message.data()); -#ifndef cimg_use_board - if (pboard) return *this; -#endif - if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety - - const float - nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)), - nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess), - nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1), - nsl2 = 1 - 2*nsl1*nspec, - nsl3 = nspec2 - nsl1 - nsl2; - - // Create light texture for phong-like rendering. - CImg<floatT> light_texture; - if (render_type==5) { - if (colors._width>primitives._width) { - static CImg<floatT> default_light_texture; - static const tc *lptr = 0; - static tc ref_values[64] = { 0 }; - const CImg<tc>& img = colors.back(); - bool is_same_texture = (lptr==img._data); - if (is_same_texture) - for (unsigned int r = 0, j = 0; j<8; ++j) - for (unsigned int i = 0; i<8; ++i) - if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) { - is_same_texture = false; break; - } - if (!is_same_texture || default_light_texture._spectrum<_spectrum) { - (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum); - lptr = colors.back().data(); - for (unsigned int r = 0, j = 0; j<8; ++j) - for (unsigned int i = 0; i<8; ++i) - ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum); - } - light_texture.assign(default_light_texture,true); - } else { - static CImg<floatT> default_light_texture; - static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0; - if (!default_light_texture || - lightx!=olightx || lighty!=olighty || lightz!=olightz || - specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) { - default_light_texture.assign(512,512); - const float - dlx = lightx - X, - dly = lighty - Y, - dlz = lightz - Z, - nl = cimg::hypot(dlx,dly,dlz), - nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl), - nly = (default_light_texture._height - 1)/2*(1 + dly/nl), - white[] = { 1 }; - default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white); - cimg_forXY(default_light_texture,x,y) { - const float factor = default_light_texture(x,y); - if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3); - } - default_light_texture.resize(-100,-100,1,_spectrum); - olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess; - } - light_texture.assign(default_light_texture,true); - } - } - - // Compute 3D to 2D projection. - CImg<tpfloat> projections(vertices._width,2); - tpfloat parallzmin = cimg::type<tpfloat>::max(); - const float absfocale = focale?cimg::abs(focale):0; - if (absfocale) { - cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) - cimg_forX(projections,l) { // Perspective projection - const tpfloat - x = (tpfloat)vertices(l,0), - y = (tpfloat)vertices(l,1), - z = (tpfloat)vertices(l,2); - const tpfloat projectedz = z + Z + absfocale; - projections(l,1) = Y + absfocale*y/projectedz; - projections(l,0) = X + absfocale*x/projectedz; - } - } else { - cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) - cimg_forX(projections,l) { // Parallel projection - const tpfloat - x = (tpfloat)vertices(l,0), - y = (tpfloat)vertices(l,1), - z = (tpfloat)vertices(l,2); - if (z<parallzmin) parallzmin = z; - projections(l,1) = Y + y; - projections(l,0) = X + x; - } - } - - const float _focale = absfocale?absfocale:(1e5f-parallzmin); - float zmax = 0; - if (zbuffer) zmax = vertices.get_shared_row(2).max(); - - // Compute visible primitives. - CImg<uintT> visibles(primitives._width,1,1,1,~0U); - CImg<tpfloat> zrange(primitives._width); - const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min(); - bool is_forward = zbuffer?true:false; - - cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096)) - cimglist_for(primitives,l) { - const CImg<tf>& primitive = primitives[l]; - switch (primitive.size()) { - case 1 : { // Point - CImg<_to> _opacity; - __draw_object3d(opacities,l,_opacity); - if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false; - const unsigned int i0 = (unsigned int)primitive(0); - const tpfloat z0 = Z + vertices(i0,2); - if (z0>zmin) { - visibles(l) = (unsigned int)l; - zrange(l) = z0; - } - } break; - case 5 : { // Sphere - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1); - const tpfloat - Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)), - Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)), - Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)), - _zc = Z + Zc, - zc = _zc + _focale, - xc = X + Xc*(absfocale?absfocale/zc:1), - yc = Y + Yc*(absfocale?absfocale/zc:1), - radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0), - vertices(i1,1) - vertices(i0,1), - vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1), - xm = xc - radius, - ym = yc - radius, - xM = xc + radius, - yM = yc + radius; - if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) { - visibles(l) = (unsigned int)l; - zrange(l) = _zc; - } - is_forward = false; - } break; - case 2 : case 6 : { // Segment - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1); - const tpfloat - x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), - x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2); - tpfloat xm, xM, ym, yM; - if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; } - if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; } - if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) { - visibles(l) = (unsigned int)l; - zrange(l) = (z0 + z1)/2; - } - } break; - case 3 : case 9 : { // Triangle - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1), - i2 = (unsigned int)primitive(2); - const tpfloat - x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), - x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), - x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2); - tpfloat xm, xM, ym, yM; - if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; } - if (x2<xm) xm = x2; - if (x2>xM) xM = x2; - if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; } - if (y2<ym) ym = y2; - if (y2>yM) yM = y2; - if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) { - const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0); - if (is_double_sided || d<0) { - visibles(l) = (unsigned int)l; - zrange(l) = (z0 + z1 + z2)/3; - } - } - } break; - case 4 : case 12 : { // Quadrangle - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1), - i2 = (unsigned int)primitive(2), - i3 = (unsigned int)primitive(3); - const tpfloat - x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), - x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), - x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2), - x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2); - tpfloat xm, xM, ym, yM; - if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; } - if (x2<xm) xm = x2; - if (x2>xM) xM = x2; - if (x3<xm) xm = x3; - if (x3>xM) xM = x3; - if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; } - if (y2<ym) ym = y2; - if (y2>yM) yM = y2; - if (y3<ym) ym = y3; - if (y3>yM) yM = y3; - if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) { - const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0); - if (is_double_sided || d<0) { - visibles(l) = (unsigned int)l; - zrange(l) = (z0 + z1 + z2 + z3)/4; - } - } - } break; - default : - if (render_type==5) cimg::mutex(10,0); - throw CImgArgumentException(_cimg_instance - "draw_object3d(): Invalid primitive[%u] with size %u " - "(should have size 1,2,3,4,5,6,9 or 12).", - cimg_instance, - l,primitive.size()); - } - } - - // Force transparent primitives to be drawn last when zbuffer is activated - // (and if object contains no spheres or sprites). - if (is_forward) - cimglist_for(primitives,l) - if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l); - - // Sort only visibles primitives. - unsigned int *p_visibles = visibles._data; - tpfloat *p_zrange = zrange._data; - const tpfloat *ptrz = p_zrange; - cimg_for(visibles,ptr,unsigned int) { - if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; } - ++ptrz; - } - const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data); - if (!nb_visibles) { - if (render_type==5) cimg::mutex(10,0); - return *this; - } - CImg<uintT> permutations; - CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward); - - // Compute light properties - CImg<floatT> lightprops; - switch (render_type) { - case 3 : { // Flat Shading - lightprops.assign(nb_visibles); - cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) - cimg_forX(lightprops,l) { - const CImg<tf>& primitive = primitives(visibles(permutations(l))); - const unsigned int psize = (unsigned int)primitive.size(); - if (psize==3 || psize==4 || psize==9 || psize==12) { - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1), - i2 = (unsigned int)primitive(2); - const tpfloat - x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), - x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), - x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), - dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, - dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, - nx = dy1*dz2 - dz1*dy2, - ny = dz1*dx2 - dx1*dz2, - nz = dx1*dy2 - dy1*dx2, - norm = 1e-5f + cimg::hypot(nx,ny,nz), - lx = X + (x0 + x1 + x2)/3 - lightx, - ly = Y + (y0 + y1 + y2)/3 - lighty, - lz = Z + (z0 + z1 + z2)/3 - lightz, - nl = 1e-5f + cimg::hypot(lx,ly,lz), - factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); - lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); - } else lightprops[l] = 1; - } - } break; - - case 4 : // Gouraud Shading - case 5 : { // Phong-Shading - CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0); - cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) - for (unsigned int l = 0; l<nb_visibles; ++l) { - const CImg<tf>& primitive = primitives[visibles(l)]; - const unsigned int psize = (unsigned int)primitive.size(); - const bool - triangle_flag = (psize==3) || (psize==9), - quadrangle_flag = (psize==4) || (psize==12); - if (triangle_flag || quadrangle_flag) { - const unsigned int - i0 = (unsigned int)primitive(0), - i1 = (unsigned int)primitive(1), - i2 = (unsigned int)primitive(2), - i3 = quadrangle_flag?(unsigned int)primitive(3):0; - const tpfloat - x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), - x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), - x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), - dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, - dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, - nnx = dy1*dz2 - dz1*dy2, - nny = dz1*dx2 - dx1*dz2, - nnz = dx1*dy2 - dy1*dx2, - norm = 1e-5f + cimg::hypot(nnx,nny,nnz), - nx = nnx/norm, - ny = nny/norm, - nz = nnz/norm; - unsigned int ix = 0, iy = 1, iz = 2; - if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; } - vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz; - vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz; - vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz; - if (quadrangle_flag) { - vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz; - } - } - } - - if (is_double_sided) cimg_forX(vertices_normals,p) { - const float - nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2), - nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5), - n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1; - if (n1>n0) { - vertices_normals(p,0) = -nx1; - vertices_normals(p,1) = -ny1; - vertices_normals(p,2) = -nz1; - } - } - - if (render_type==4) { - lightprops.assign(vertices._width); - cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) - cimg_forX(lightprops,l) { - const tpfloat - nx = vertices_normals(l,0), - ny = vertices_normals(l,1), - nz = vertices_normals(l,2), - norm = 1e-5f + cimg::hypot(nx,ny,nz), - lx = X + vertices(l,0) - lightx, - ly = Y + vertices(l,1) - lighty, - lz = Z + vertices(l,2) - lightz, - nl = 1e-5f + cimg::hypot(lx,ly,lz), - factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); - lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); - } - } else { - const unsigned int - lw2 = light_texture._width/2 - 1, - lh2 = light_texture._height/2 - 1; - lightprops.assign(vertices._width,2); - cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) - cimg_forX(lightprops,l) { - const tpfloat - nx = vertices_normals(l,0), - ny = vertices_normals(l,1), - nz = vertices_normals(l,2), - norm = 1e-5f + cimg::hypot(nx,ny,nz), - nnx = nx/norm, - nny = ny/norm; - lightprops(l,0) = lw2*(1 + nnx); - lightprops(l,1) = lh2*(1 + nny); - } - } - } break; - } - - // Draw visible primitives - const CImg<tc> default_color(1,_spectrum,1,1,(tc)200); - CImg<_to> _opacity; - - for (unsigned int l = 0; l<nb_visibles; ++l) { - const unsigned int n_primitive = visibles(permutations(l)); - const CImg<tf>& primitive = primitives[n_primitive]; - const CImg<tc> - &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(), - _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)? - __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(), - &color = _color?_color:(__color?__color:default_color); - const tc *const pcolor = color._data; - float opacity = __draw_object3d(opacities,n_primitive,_opacity); - if (_opacity.is_empty()) opacity*=g_opacity; - else if (!_opacity.is_shared()) _opacity*=g_opacity; - -#ifdef cimg_use_board - LibBoard::Board &board = *(LibBoard::Board*)pboard; -#endif - - switch (primitive.size()) { - case 1 : { // Colored point or sprite - const unsigned int n0 = (unsigned int)primitive[0]; - const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1); - - if (_opacity.is_empty()) { // Scalar opacity - - if (color.size()==_spectrum) { // Colored point - draw_point(x0,y0,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawDot((float)x0,height()-(float)y0); - } -#endif - } else { // Sprite - const tpfloat z = Z + vertices(n0,2); - const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); - const unsigned int - _sw = (unsigned int)(color._width*factor), - _sh = (unsigned int)(color._height*factor), - sw = _sw?_sw:1, sh = _sh?_sh:1; - const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; - if (sw<=3*_width/2 && sh<=3*_height/2 && - (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) { - const CImg<tc> - _sprite = (sw!=color._width || sh!=color._height)? - color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(), - &sprite = _sprite?_sprite:color; - draw_image(nx0,ny0,sprite,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128); - board.setFillColor(LibBoard::Color::Null); - board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); - } -#endif - } - } - } else { // Opacity mask - const tpfloat z = Z + vertices(n0,2); - const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); - const unsigned int - _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor), - _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor), - sw = _sw?_sw:1, sh = _sh?_sh:1; - const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; - if (sw<=3*_width/2 && sh<=3*_height/2 && - (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) { - const CImg<tc> - _sprite = (sw!=color._width || sh!=color._height)? - color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(), - &sprite = _sprite?_sprite:color; - const CImg<_to> - _nopacity = (sw!=_opacity._width || sh!=_opacity._height)? - _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(), - &nopacity = _nopacity?_nopacity:_opacity; - draw_image(nx0,ny0,sprite,nopacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128); - board.setFillColor(LibBoard::Color::Null); - board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); - } -#endif - } - } - } break; - case 2 : { // Colored line - const unsigned int - n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1]; - const int - x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), - x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); - const float - z0 = vertices(n0,2) + Z + _focale, - z1 = vertices(n1,2) + Z + _focale; - if (render_type) { - if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity); - else draw_line(x0,y0,x1,y1,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1); - } -#endif - } else { - draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawDot((float)x0,height() - (float)y0); - board.drawDot((float)x1,height() - (float)y1); - } -#endif - } - } break; - case 5 : { // Colored sphere - const unsigned int - n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1], - is_wireframe = (unsigned int)primitive[2]; - const float - Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)), - Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)), - Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)), - zc = Z + Zc + _focale, - xc = X + Xc*(absfocale?absfocale/zc:1), - yc = Y + Yc*(absfocale?absfocale/zc:1), - radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0), - vertices(n1,1) - vertices(n0,1), - vertices(n1,2) - vertices(n0,2))*(absfocale?absfocale/zc:1); - switch (render_type) { - case 0 : - draw_point((int)xc,(int)yc,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawDot(xc,height() - yc); - } -#endif - break; - case 1 : - draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.setFillColor(LibBoard::Color::Null); - board.drawCircle(xc,height() - yc,radius); - } -#endif - break; - default : - if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); - else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - if (!is_wireframe) board.fillCircle(xc,height() - yc,radius); - else { - board.setFillColor(LibBoard::Color::Null); - board.drawCircle(xc,height() - yc,radius); - } - } -#endif - break; - } - } break; - case 6 : { // Textured line - if (!__color) { - if (render_type==5) cimg::mutex(10,0); - throw CImgArgumentException(_cimg_instance - "draw_object3d(): Undefined texture for line primitive [%u].", - cimg_instance,n_primitive); - } - const unsigned int - n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1]; - const int - tx0 = (int)primitive[2], ty0 = (int)primitive[3], - tx1 = (int)primitive[4], ty1 = (int)primitive[5], - x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), - x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); - const float - z0 = vertices(n0,2) + Z + _focale, - z1 = vertices(n1,2) + Z + _focale; - if (render_type) { - if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); - else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); - } -#endif - } else { - draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, - ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). - draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, - ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.drawDot((float)x0,height() - (float)y0); - board.drawDot((float)x1,height() - (float)y1); - } -#endif - } - } break; - case 3 : { // Colored triangle - const unsigned int - n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1], - n2 = (unsigned int)primitive[2]; - const int - x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), - x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), - x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); - const float - z0 = vertices(n0,2) + Z + _focale, - z1 = vertices(n1,2) + Z + _focale, - z2 = vertices(n2,2) + Z + _focale; - switch (render_type) { - case 0 : - draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawDot((float)x0,height() - (float)y0); - board.drawDot((float)x1,height() - (float)y1); - board.drawDot((float)x2,height() - (float)y2); - } -#endif - break; - case 1 : - if (zbuffer) - draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity). - draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity); - else - draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity). - draw_line(x1,y1,x2,y2,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); - board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); - board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); - } -#endif - break; - case 2 : - if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity); - else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - } -#endif - break; - case 3 : - if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)); - else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)); -#ifdef cimg_use_board - if (pboard) { - const float lp = std::min(lightprops(l),1.f); - board.setPenColorRGBi((unsigned char)(color[0]*lp), - (unsigned char)(color[1]*lp), - (unsigned char)(color[2]*lp), - (unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - } -#endif - break; - case 4 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor, - lightprops(n0),lightprops(n1),lightprops(n2),opacity); - else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi((unsigned char)(color[0]), - (unsigned char)(color[1]), - (unsigned char)(color[2]), - (unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), - (float)x1,height() - (float)y1,lightprops(n1), - (float)x2,height() - (float)y2,lightprops(n2)); - } -#endif - break; - case 5 : { - const unsigned int - lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), - lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), - lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1); - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); - else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); -#ifdef cimg_use_board - if (pboard) { - const float - l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), - (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), - l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), - (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), - l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), - (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); - board.setPenColorRGBi((unsigned char)(color[0]), - (unsigned char)(color[1]), - (unsigned char)(color[2]), - (unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, - (float)x1,height() - (float)y1,l1, - (float)x2,height() - (float)y2,l2); - } -#endif - } break; - } - } break; - case 4 : { // Colored quadrangle - const unsigned int - n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1], - n2 = (unsigned int)primitive[2], - n3 = (unsigned int)primitive[3]; - const int - x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), - x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), - x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), - x3 = (int)projections(n3,0), y3 = (int)projections(n3,1), - xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4; - const float - z0 = vertices(n0,2) + Z + _focale, - z1 = vertices(n1,2) + Z + _focale, - z2 = vertices(n2,2) + Z + _focale, - z3 = vertices(n3,2) + Z + _focale, - zc = (z0 + z1 + z2 + z3)/4; - - switch (render_type) { - case 0 : - draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity). - draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawDot((float)x0,height() - (float)y0); - board.drawDot((float)x1,height() - (float)y1); - board.drawDot((float)x2,height() - (float)y2); - board.drawDot((float)x3,height() - (float)y3); - } -#endif - break; - case 1 : - if (zbuffer) - draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity). - draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity); - else - draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity). - draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); - board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); - board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); - board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); - } -#endif - break; - case 2 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity); - else - draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x2,height() - (float)y2, - (float)x3,height() - (float)y3); - } -#endif - break; - case 3 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l)); - else - _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)). - _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l)); -#ifdef cimg_use_board - if (pboard) { - const float lp = std::min(lightprops(l),1.f); - board.setPenColorRGBi((unsigned char)(color[0]*lp), - (unsigned char)(color[1]*lp), - (unsigned char)(color[2]*lp),(unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x2,height() - (float)y2, - (float)x3,height() - (float)y3); - } -#endif - break; - case 4 : { - const float - lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), - lightprop2 = lightprops(n2), lightprop3 = lightprops(n3), - lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4; - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity). - draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity). - draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity). - draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity); - else - draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity). - draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity). - draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity). - draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity); - -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi((unsigned char)(color[0]), - (unsigned char)(color[1]), - (unsigned char)(color[2]), - (unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, - (float)x1,height() - (float)y1,lightprop1, - (float)x2,height() - (float)y2,lightprop2); - board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, - (float)x2,height() - (float)y2,lightprop2, - (float)x3,height() - (float)y3,lightprop3); - } -#endif - } break; - case 5 : { - const unsigned int - lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), - lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), - lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), - lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1), - lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4; - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). - draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). - draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). - draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); - else - draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). - draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). - draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). - draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); - -#ifdef cimg_use_board - if (pboard) { - const float - l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), - l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), - l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), - l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); - board.setPenColorRGBi((unsigned char)(color[0]), - (unsigned char)(color[1]), - (unsigned char)(color[2]), - (unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, - (float)x1,height() - (float)y1,l1, - (float)x2,height() - (float)y2,l2); - board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, - (float)x2,height() - (float)y2,l2, - (float)x3,height() - (float)y3,l3); - } -#endif - } break; - } - } break; - case 9 : { // Textured triangle - if (!__color) { - if (render_type==5) cimg::mutex(10,0); - throw CImgArgumentException(_cimg_instance - "draw_object3d(): Undefined texture for triangle primitive [%u].", - cimg_instance,n_primitive); - } - const unsigned int - n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1], - n2 = (unsigned int)primitive[2]; - const int - tx0 = (int)primitive[3], ty0 = (int)primitive[4], - tx1 = (int)primitive[5], ty1 = (int)primitive[6], - tx2 = (int)primitive[7], ty2 = (int)primitive[8], - x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), - x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), - x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); - const float - z0 = vertices(n0,2) + Z + _focale, - z1 = vertices(n1,2) + Z + _focale, - z2 = vertices(n2,2) + Z + _focale; - switch (render_type) { - case 0 : - draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, - ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). - draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, - ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). - draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, - ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.drawDot((float)x0,height() - (float)y0); - board.drawDot((float)x1,height() - (float)y1); - board.drawDot((float)x2,height() - (float)y2); - } -#endif - break; - case 1 : - if (zbuffer) - draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). - draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). - draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); - else - draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). - draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). - draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); - board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); - board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); - } -#endif - break; - case 2 : - if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); - else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - } -#endif - break; - case 3 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); - else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); -#ifdef cimg_use_board - if (pboard) { - const float lp = std::min(lightprops(l),1.f); - board.setPenColorRGBi((unsigned char)(128*lp), - (unsigned char)(128*lp), - (unsigned char)(128*lp), - (unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - } -#endif - break; - case 4 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, - lightprops(n0),lightprops(n1),lightprops(n2),opacity); - else - draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, - lightprops(n0),lightprops(n1),lightprops(n2),opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), - (float)x1,height() - (float)y1,lightprops(n1), - (float)x2,height() - (float)y2,lightprops(n2)); - } -#endif - break; - case 5 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, - (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), - (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), - (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), - opacity); - else - draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, - (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), - (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), - (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), - opacity); -#ifdef cimg_use_board - if (pboard) { - const float - l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), - (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), - l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), - (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), - l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), - (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, - (float)x1,height() - (float)y1,l1, - (float)x2,height() - (float)y2,l2); - } -#endif - break; - } - } break; - case 12 : { // Textured quadrangle - if (!__color) { - if (render_type==5) cimg::mutex(10,0); - throw CImgArgumentException(_cimg_instance - "draw_object3d(): Undefined texture for quadrangle primitive [%u].", - cimg_instance,n_primitive); - } - const unsigned int - n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1], - n2 = (unsigned int)primitive[2], - n3 = (unsigned int)primitive[3]; - const int - tx0 = (int)primitive[4], ty0 = (int)primitive[5], - tx1 = (int)primitive[6], ty1 = (int)primitive[7], - tx2 = (int)primitive[8], ty2 = (int)primitive[9], - tx3 = (int)primitive[10], ty3 = (int)primitive[11], - txc = (tx0 + tx1 + tx2 + tx3)/4, tyc = (ty0 + ty1 + ty2 + ty3)/4, - x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), - x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), - x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), - x3 = (int)projections(n3,0), y3 = (int)projections(n3,1), - xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4; - const float - z0 = vertices(n0,2) + Z + _focale, - z1 = vertices(n1,2) + Z + _focale, - z2 = vertices(n2,2) + Z + _focale, - z3 = vertices(n3,2) + Z + _focale, - zc = (z0 + z1 + z2 + z3)/4; - - switch (render_type) { - case 0 : - draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, - ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). - draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, - ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). - draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, - ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity). - draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3, - ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.drawDot((float)x0,height() - (float)y0); - board.drawDot((float)x1,height() - (float)y1); - board.drawDot((float)x2,height() - (float)y2); - board.drawDot((float)x3,height() - (float)y3); - } -#endif - break; - case 1 : - if (zbuffer) - draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). - draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). - draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). - draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); - else - draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). - draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). - draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). - draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); - board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); - board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); - board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); - } -#endif - break; - case 2 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); - else - draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). - draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x2,height() - (float)y2, - (float)x3,height() - (float)y3); - } -#endif - break; - case 3 : - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); - else - draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). - draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); -#ifdef cimg_use_board - if (pboard) { - const float lp = std::min(lightprops(l),1.f); - board.setPenColorRGBi((unsigned char)(128*lp), - (unsigned char)(128*lp), - (unsigned char)(128*lp), - (unsigned char)(opacity*255)); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x1,height() - (float)y1, - (float)x2,height() - (float)y2); - board.fillTriangle((float)x0,height() - (float)y0, - (float)x2,height() - (float)y2, - (float)x3,height() - (float)y3); - } -#endif - break; - case 4 : { - const float - lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), - lightprop2 = lightprops(n2), lightprop3 = lightprops(n3), - lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop3)/4; - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc, - lightprop0,lightprop1,lightpropc,opacity). - draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc, - lightprop1,lightprop2,lightpropc,opacity). - draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc, - lightprop2,lightprop3,lightpropc,opacity). - draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc, - lightprop3,lightprop0,lightpropc,opacity); - else - draw_triangle(x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc, - lightprop0,lightprop1,lightpropc,opacity). - draw_triangle(x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc, - lightprop1,lightprop2,lightpropc,opacity). - draw_triangle(x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc, - lightprop2,lightprop3,lightpropc,opacity). - draw_triangle(x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc, - lightprop3,lightprop0,lightpropc,opacity); -#ifdef cimg_use_board - if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, - (float)x1,height() - (float)y1,lightprop1, - (float)x2,height() - (float)y2,lightprop2); - board.fillGouraudTriangle((float)x0,height() -(float)y0,lightprop0, - (float)x2,height() - (float)y2,lightprop2, - (float)x3,height() - (float)y3,lightprop3); - } -#endif - } break; - case 5 : { - const unsigned int - lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), - lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), - lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), - lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1), - lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4; - if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc, - light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). - draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc, - light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). - draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc, - light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). - draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc, - light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); - else - draw_triangle(x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc, - light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). - draw_triangle(x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc, - light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). - draw_triangle(x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc, - light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). - draw_triangle(x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc, - light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); -#ifdef cimg_use_board - if (pboard) { - const float - l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), - l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), - l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), - l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); - board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); - board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, - (float)x1,height() - (float)y1,l1, - (float)x2,height() - (float)y2,l2); - board.fillGouraudTriangle((float)x0,height() -(float)y0,l0, - (float)x2,height() - (float)y2,l2, - (float)x3,height() - (float)y3,l3); - } -#endif - } break; - } - } break; - } - } - if (render_type==5) cimg::mutex(10,0); - return *this; - } - - //@} - //--------------------------- - // - //! \name Data Input - //@{ - //--------------------------- - - //! Launch simple interface to select a shape from an image. - /** - \param disp Display window to use. - \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>. - \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images. - \param exit_on_anykey Exit function when any key is pressed. - **/ - CImg<T>& select(CImgDisplay &disp, - const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false, - const bool is_deep_selection_default=false) { - return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); - } - - //! Simple interface to select a shape from an image \overloading. - CImg<T>& select(const char *const title, - const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false, - const bool is_deep_selection_default=false) { - return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); - } - - //! Simple interface to select a shape from an image \newinstance. - CImg<intT> get_select(CImgDisplay &disp, - const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false, - const bool is_deep_selection_default=false) const { - return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); - } - - //! Simple interface to select a shape from an image \newinstance. - CImg<intT> get_select(const char *const title, - const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false, - const bool is_deep_selection_default=false) const { - CImgDisplay disp; - return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); - } - - CImg<intT> _select(CImgDisplay &disp, const char *const title, - const unsigned int feature_type, unsigned int *const XYZ, - const int origX, const int origY, const int origZ, - const bool exit_on_anykey, - const bool reset_view3d, - const bool force_display_z_coord, - const bool is_deep_selection_default) const { - if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1); - if (!disp) { - disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); - if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); - } else if (title) disp.set_title("%s",title); - - CImg<T> thumb; - if (width()>disp.screen_width() || height()>disp.screen_height()) - get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb); - - const unsigned int old_normalization = disp.normalization(); - bool old_is_resized = disp.is_resized(); - disp._normalization = 0; - disp.show().set_key(0).set_wheel().show_mouse(); - - static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; - - int area = 0, area_started = 0, area_clicked = 0, phase = 0, - X0 = (int)((XYZ?XYZ[0]:(_width - 1)/2)%_width), - Y0 = (int)((XYZ?XYZ[1]:(_height - 1)/2)%_height), - Z0 = (int)((XYZ?XYZ[2]:(_depth - 1)/2)%_depth), - X1 =-1, Y1 = -1, Z1 = -1, - X3d = -1, Y3d = -1, - oX3d = X3d, oY3d = -1, - omx = -1, omy = -1; - float X = -1, Y = -1, Z = -1; - unsigned int key = 0; - - bool is_deep_selection = is_deep_selection_default, - shape_selected = false, text_down = false, visible_cursor = true; - static CImg<floatT> pose3d; - static bool is_view3d = false, is_axes = true; - if (reset_view3d) { pose3d.assign(); is_view3d = false; } - CImg<floatT> points3d, opacities3d, sel_opacities3d; - CImgList<uintT> primitives3d, sel_primitives3d; - CImgList<ucharT> colors3d, sel_colors3d; - CImg<ucharT> visu, visu0, view3d; - CImg<charT> text(1024); *text = 0; - - while (!key && !disp.is_closed() && !shape_selected) { - - // Handle mouse motion and selection - int - mx = disp.mouse_x(), - my = disp.mouse_y(); - - const float - mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(), - mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height(); - - area = 0; - if (mX>=0 && mY>=0 && mX<width() && mY<height()) { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); } - if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); } - if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); } - if (mX>=width() && mY>=height()) area = 4; - if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0; - - CImg<charT> filename(32); - - switch (key = disp.key()) { -#if cimg_OS!=2 - case cimg::keyCTRLRIGHT : -#endif - case 0 : case cimg::keyCTRLLEFT : key = 0; break; - case cimg::keyPAGEUP : - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break; - case cimg::keyPAGEDOWN : - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break; - case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), - CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). - _is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - static unsigned int snap_number = 0; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - if (visu0) { - (+visu0).__draw_text(" Saving snapshot...",text_down).display(disp); - visu0.save(filename); - (+visu0).__draw_text(" Snapshot '%s' saved. ",text_down,filename._data).display(disp); - } - disp.set_key(key,false); key = 0; - } break; - case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - static unsigned int snap_number = 0; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); -#endif - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+visu0).__draw_text(" Saving instance... ",text_down).display(disp); - save(filename); - (+visu0).__draw_text(" Instance '%s' saved. ",text_down,filename._data).display(disp); - disp.set_key(key,false); key = 0; - } break; - } - - switch (area) { - - case 0 : // When mouse is out of image range - mx = my = -1; X = Y = Z = -1; - break; - - case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections - const unsigned int but = disp.button(); - const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4); - - if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step) - if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); - X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; - } - if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes) - switch (area_started) { - case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break; - case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break; - case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break; - } - } - if (b2 && area_clicked==area) { // When moving through the image/volume - if (phase) { - if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); - X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; - } else { - if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign(); - X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; - } - } - if (b3) { // Reset selection - X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0; - visu0.assign(); - } - if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel) - if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && - !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) { - switch (area) { - case 1 : - if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel()); - visu0.assign(); break; - case 2 : - if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel()); - visu0.assign(); break; - case 3 : - if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel()); - visu0.assign(); break; - } - disp.set_wheel(); - } else key = ~0U; - } - - if ((phase==0 && b1) || - (phase==1 && !b1) || - (phase==2 && b1)) switch (phase) { // Detect change of phase - case 0 : - if (area==area_clicked) { - X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase; - } break; - case 1 : - if (area==area_started) { - X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase; - if (_depth>1) { - if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default; - if (is_deep_selection) ++phase; - } - } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); } - break; - case 2 : ++phase; break; - } - } break; - - case 4 : // When mouse is over the 3D view - if (is_view3d && points3d) { - X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0)); - Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0)); - if (oX3d<0) { oX3d = X3d; oY3d = Y3d; } - // Left + right buttons: reset. - if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } - else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate - const float - R = 0.45f*std::min(view3d._width,view3d._height), - R2 = R*R, - u0 = (float)(oX3d - view3d.width()/2), - v0 = (float)(oY3d - view3d.height()/2), - u1 = (float)(X3d - view3d.width()/2), - v1 = (float)(Y3d - view3d.height()/2), - n0 = cimg::hypot(u0,v0), - n1 = cimg::hypot(u1,v1), - nu0 = n0>R?(u0*R/n0):u0, - nv0 = n0>R?(v0*R/n0):v0, - nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), - nu1 = n1>R?(u1*R/n1):u1, - nv1 = n1>R?(v1*R/n1):v1, - nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), - u = nv0*nw1 - nw0*nv1, - v = nw0*nu1 - nu0*nw1, - w = nv0*nu1 - nu0*nv1, - n = cimg::hypot(u,v,w), - alpha = (float)std::asin(n/R2)*180/cimg::PI; - pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2)); - view3d.assign(); - } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom - pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign(); - } - if (disp.wheel()) { // Wheel: zoom - pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel(); - } - if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift - pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign(); - } - oX3d = X3d; oY3d = Y3d; - } - mx = my = -1; X = Y = Z = -1; - break; - } - - if (phase) { - if (!feature_type) shape_selected = phase?true:false; - else { - if (_depth>1) shape_selected = (phase==3)?true:false; - else shape_selected = (phase==2)?true:false; - } - } - - if (X0<0) X0 = 0; - if (X0>=width()) X0 = width() - 1; - if (Y0<0) Y0 = 0; - if (Y0>=height()) Y0 = height() - 1; - if (Z0<0) Z0 = 0; - if (Z0>=depth()) Z0 = depth() - 1; - if (X1<1) X1 = 0; - if (X1>=width()) X1 = width() - 1; - if (Y1<0) Y1 = 0; - if (Y1>=height()) Y1 = height() - 1; - if (Z1<0) Z1 = 0; - if (Z1>=depth()) Z1 = depth() - 1; - - // Draw visualization image on the display - if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { - - if (!visu0) { // Create image of projected planes - if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); - else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); - visu0.resize(disp); - view3d.assign(); - points3d.assign(); - } - - if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images - const unsigned int - _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1), - _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1), - x3d = _x3d>=visu0._width?visu0._width - 1:_x3d, - y3d = _y3d>=visu0._height?visu0._height - 1:_y3d; - CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3). - move_to(view3d); - if (!points3d) { - get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d); - points3d.append(CImg<floatT>(8,3,1,1, - 0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0, - 0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1, - 0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x'); - CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d); - CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d); - CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d); - CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d); - CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d); - CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d); - colors3d.insert(12,CImg<ucharT>::vector(255,255,255)); - opacities3d.assign(primitives3d.width(),1,1,1,0.5f); - if (!phase) { - opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f; - sel_primitives3d.assign(); - sel_colors3d.assign(); - sel_opacities3d.assign(); - } else { - if (feature_type==2) { - points3d.append(CImg<floatT>(8,3,1,1, - X0,X1,X1,X0,X0,X1,X1,X0, - Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1, - Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x'); - sel_primitives3d.assign(); - CImg<uintT>::vector(20,21).move_to(sel_primitives3d); - CImg<uintT>::vector(21,22).move_to(sel_primitives3d); - CImg<uintT>::vector(22,23).move_to(sel_primitives3d); - CImg<uintT>::vector(23,20).move_to(sel_primitives3d); - CImg<uintT>::vector(24,25).move_to(sel_primitives3d); - CImg<uintT>::vector(25,26).move_to(sel_primitives3d); - CImg<uintT>::vector(26,27).move_to(sel_primitives3d); - CImg<uintT>::vector(27,24).move_to(sel_primitives3d); - CImg<uintT>::vector(20,24).move_to(sel_primitives3d); - CImg<uintT>::vector(21,25).move_to(sel_primitives3d); - CImg<uintT>::vector(22,26).move_to(sel_primitives3d); - CImg<uintT>::vector(23,27).move_to(sel_primitives3d); - } else { - points3d.append(CImg<floatT>(2,3,1,1, - X0,X1, - Y0,Y1, - Z0,Z1),'x'); - sel_primitives3d.assign(CImg<uintT>::vector(20,21)); - } - sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255)); - sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f); - } - points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d(); - points3d*=0.75f*std::min(view3d._width,view3d._height); - } - - if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d); - CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0); - const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d; - if (sel_primitives3d) - view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, - pose3d(3,1) + 0.5f*view3d._height, - pose3d(3,2), - rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d, - 2,true,500,0,0,0,0,0,1,zbuffer3d); - view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, - pose3d(3,1) + 0.5f*view3d._height, - pose3d(3,2), - rotated_points3d,primitives3d,colors3d,opacities3d, - 2,true,500,0,0,0,0,0,1,zbuffer3d); - visu0.draw_image(x3d,y3d,view3d); - } - visu = visu0; - - if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} - else { - if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }} - else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} - const int d = (depth()>1)?depth():0; - int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z; - if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; } - int - w = disp.width(), W = width() + d, - h = disp.height(), H = height() + d, - _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX), - _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY), - _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1), - _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1), - _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()), - _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()), - _zxn = (int)((_vZ + width() + 1.f)*w/W - 1), - zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1), - _zyn = (int)((_vZ + height() + 1.f)*h/H - 1), - zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1), - _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()), - _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()), - xc = (xp + xn)/2, - yc = (yp + yn)/2, - zxc = (zxp + zxn)/2, - zyc = (zyp + zyn)/2, - xf = (int)(X*w/W), - yf = (int)(Y*h/H), - zxf = (int)((Z + width())*w/W), - zyf = (int)((Z + height())*h/H); - - if (is_axes) { // Draw axes - visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00). - draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF). - draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00). - draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF); - if (_depth>1) - visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00). - draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF). - draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00). - draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF); - } - - // Draw box cursor. - if (xn - xp>=4 && yn - yp>=4) - visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f). - draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555); - if (_depth>1) { - if (yn - yp>=4 && zxn - zxp>=4) - visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f). - draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555); - if (xn - xp>=4 && zyn - zyp>=4) - visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f). - draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555); - } - - // Draw selection. - if (phase && (phase!=1 || area_started==area)) { - const int - _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0), - _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0), - _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1), - _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1), - _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()), - _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()), - _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1), - zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1), - _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1), - zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1), - xc0 = (xp0 + xn0)/2, - yc0 = (yp0 + yn0)/2, - zxc0 = (zxp0 + zxn0)/2, - zyc0 = (zyp0 + zyn0)/2; - - switch (feature_type) { - case 1 : { - visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x55555555). - draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA); - if (d) { - visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x55555555). - draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA). - draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x55555555). - draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xAAAAAAAA); - } - } break; - case 2 : { - visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f). - draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555). - draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA). - draw_arrow(xc0,yc0,xc,yc,background_color,0.5f,30,5,0x55555555). - draw_arrow(xc0,yc0,xc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA); - if (d) { - visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f). - draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0, - background_color,0.9f,0x55555555). - draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0, - foreground_color,0.9f,0xAAAAAAAA). - draw_arrow(zxc0,yc0,zxc,yc,background_color,0.5f,30,5,0x55555555). - draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA). - draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0, - background_color,0.2f). - draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0, - background_color,0.9f,0x55555555). - draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0, - foreground_color,0.9f,0xAAAAAAAA). - draw_arrow(xp0,zyp0,xn,zyn,background_color,0.5f,30,5,0x55555555). - draw_arrow(xp0,zyp0,xn,zyn,foreground_color,0.5f,30,5,0xAAAAAAAA); - } - } break; - case 3 : { - visu.draw_ellipse(xc0,yc0, - (float)cimg::abs(xc - xc0), - (float)cimg::abs(yc - yc0),0,background_color,0.2f). - draw_ellipse(xc0,yc0, - (float)cimg::abs(xc - xc0), - (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U). - draw_point(xc0,yc0,foreground_color,0.9f); - if (d) { - visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0, - background_color,0.2f). - draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0, - foreground_color,0.9f,~0U). - draw_point(zxc0,yc0,foreground_color,0.9f). - draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0, - background_color,0.2f). - draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0, - foreground_color,0.9f,~0U). - draw_point(xc0,zyc0,foreground_color,0.9f); - } - } break; - } - } - - // Draw text info. - if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false; - if (!feature_type || !phase) { - if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) { - if (_depth>1 || force_display_z_coord) - cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z); - else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y); - CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z); - const bool is_large_spectrum = values._height>16; - if (is_large_spectrum) - values.draw_image(0,8,values.get_rows(values._height - 8,values._height - 1)).resize(1,16,1,1,0); - char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512; - for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) { - cimg_snprintf(ctext,24,cimg::type<T>::format_s(), - cimg::type<T>::format(values[c])); - ctext += std::strlen(ctext); - if (c==7 && is_large_spectrum) { - cimg_snprintf(ctext,24," (...)"); - ctext += std::strlen(ctext); - } - *(ctext++) = ' '; *ctext = 0; - } - std::strcpy(text._data + std::strlen(text),"] "); - } - } else switch (feature_type) { - case 1 : { - const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), - length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); - if (_depth>1 || force_display_z_coord) - cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ", - origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length); - else if (_width!=1 && _height!=1) - cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ", - origX + X0,origY + Y0,origX + X1,origY + Y1,length, - cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1)); - else - cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ", - origX + X0,origY + Y0,origX + X1,origY + Y1,length); - } break; - case 2 : { - const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), - length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); - if (_depth>1 || force_display_z_coord) - cimg_snprintf(text,text._width, - " Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d), Length = %g ", - origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1), - origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0), - 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length); - else if (_width!=1 && _height!=1) - cimg_snprintf(text,text._width, - " Box (%d,%d)-(%d,%d), Size = (%d,%d), Length = %g, Angle = %g\260 ", - origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1), - origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0), - 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length, - cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1)); - else - cimg_snprintf(text,text._width, - " Box (%d,%d)-(%d,%d), Size = (%d,%d), Length = %g ", - origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1), - origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0), - 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length); - } break; - default : - if (_depth>1 || force_display_z_coord) - cimg_snprintf(text,text._width," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ", - origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1, - 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1)); - else cimg_snprintf(text,text._width," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ", - origX + X0,origY + Y0,origX + X1,origY + Y1, - 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1)); - } - if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",text_down,text._data); - } - - disp.display(visu); - } - if (!shape_selected) disp.wait(); - if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); } - omx = mx; omy = my; - if (!exit_on_anykey && key && key!=cimg::keyESC && - (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - key = 0; - } - } - - // Return result. - CImg<intT> res(1,feature_type==0?3:6,1,1,-1); - if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } - if (shape_selected) { - if (feature_type==2) { - if (is_deep_selection) switch (area_started) { - case 1 : Z0 = 0; Z1 = _depth - 1; break; - case 2 : Y0 = 0; Y1 = _height - 1; break; - case 3 : X0 = 0; X1 = _width - 1; break; - } - if (X0>X1) cimg::swap(X0,X1); - if (Y0>Y1) cimg::swap(Y0,Y1); - if (Z0>Z1) cimg::swap(Z0,Z1); - } - if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; - switch (feature_type) { - case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; - case 3 : - res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0); - res[0] = X0; res[1] = Y0; res[2] = Z0; - break; - default : res[0] = X0; res[1] = Y0; res[2] = Z0; - } - } - if (!exit_on_anykey || !(disp.button()&4)) disp.set_button(); - if (!visible_cursor) disp.show_mouse(); - disp._normalization = old_normalization; - disp._is_resized = old_is_resized; - if (key!=~0U) disp.set_key(key); - return res; - } - - // Return a visualizable uchar8 image for display routines. - CImg<ucharT> _get_select(const CImgDisplay& disp, const int normalization, - const int x, const int y, const int z) const { - if (is_empty()) return CImg<ucharT>(1,1,1,1,0); - const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1)); - CImg<Tuchar> img2d; - if (_depth>1) { - const int mdisp = std::min(disp.screen_width(),disp.screen_height()); - if (depth()>mdisp) { - crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d); - img2d.projections2d(x,y,z*img2d._depth/_depth); - } else crop.get_projections2d(x,y,z).move_to(img2d); - } else CImg<Tuchar>(crop,false).move_to(img2d); - - // Check for inf and NaN values. - if (cimg::type<T>::is_float() && normalization) { - bool is_inf = false, is_nan = false; - cimg_for(img2d,ptr,Tuchar) - if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; } - else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; } - if (is_inf || is_nan) { - Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min(); - if (!normalization) { m0 = 0; M0 = 255; } - else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; } - else - cimg_for(img2d,ptr,Tuchar) - if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) { - if (*ptr<(Tuchar)m0) m0 = *ptr; - if (*ptr>(Tuchar)M0) M0 = *ptr; - } - const T - val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0), - val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0); - if (is_nan) - cimg_for(img2d,ptr,Tuchar) - if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values - if (is_inf) - cimg_for(img2d,ptr,Tuchar) - if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values - } - } - - switch (normalization) { - case 1 : img2d.normalize((ucharT)0,(ucharT)255); break; - case 2 : { - const float m = disp._min, M = disp._max; - (img2d-=m)*=255.f/(M - m>0?M - m:1); - } break; - case 3 : - if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255); - else { - const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max(); - (img2d-=m)*=255.f/(M - m>0?M - m:1); - } break; - } - if (img2d.spectrum()==2) img2d.channels(0,2); - return img2d; - } - - //! Select sub-graph in a graph. - CImg<intT> get_select_graph(CImgDisplay &disp, - const unsigned int plot_type=1, const unsigned int vertex_type=1, - const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0, - const bool exit_on_anykey=false) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "select_graph(): Empty instance.", - cimg_instance); - if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). - set_title("CImg<%s>",pixel_type()); - const ulongT siz = (ulongT)_width*_height*_depth; - const unsigned int old_normalization = disp.normalization(); - disp.show().set_button().set_wheel()._normalization = 0; - - double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax; - if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; } - if (nymin==nymax) { --nymin; ++nymax; } - if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; } - - static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; - static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 }; - - CImg<ucharT> colormap(3,_spectrum); - if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; } - else { - colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10; - if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; } - if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; } - if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10; } - if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10; colormap(2,4) = 220; } - if (_spectrum>5) { colormap(0,5) = 10; colormap(1,5) = 220; colormap(2,5) = 220; } - if (_spectrum>6) { - ulongT rng = 10; - cimg_for_inY(colormap,6,colormap.height()-1,k) { - colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); - colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); - colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); - } - } - } - - CImg<ucharT> visu0, visu, graph, text, axes; - int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2; - const unsigned int one = plot_type==3?0U:1U; - unsigned int okey = 0, obutton = 0; - CImg<charT> message(1024); - CImg_3x3(I,unsigned char); - - for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) { - const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); - const unsigned int key = disp.key(), button = disp.button(); - - // Generate graph representation. - if (!visu0) { - visu0.assign(disp.width(),disp.height(),1,3,220); - const int gdimx = disp.width() - 32, gdimy = disp.height() - 32; - if (gdimx>0 && gdimy>0) { - graph.assign(gdimx,gdimy,1,3,255); - if (siz<32) { - if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0, - false,true,black,0.2f,0x33333333,0x33333333); - } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333); - cimg_forC(*this,c) - graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f, - plot_type,vertex_type,nymax,nymin); - - axes.assign(gdimx,gdimy,1,1,0); - const float - dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin), - px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.), - py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.); - const CImg<Tdouble> - seqx = dx<=0?CImg<Tdouble>::vector(nxmin): - CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz).round(px), - seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py); - - const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0); - axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero); - if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero); - if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero); - if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero); - if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero); - - cimg_for3x3(axes,x,y,0,0,I,unsigned char) - if (Icc) { - if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0; - else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3); - } - else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) - cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3); - - visu0.draw_image(16,16,graph); - visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2). - draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white); - } else graph.assign(); - text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3); - visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text); - text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3); - visu0.draw_image(1,(visu0.height() - text.height())/2,~text); - visu.assign(); - } - - // Generate and display current view. - if (!visu) { - visu.assign(visu0); - if (graph && x0>=0 && x1>=0) { - const int - nx0 = x0<=x1?x0:x1, - nx1 = x0<=x1?x1:x0, - ny0 = y0<=y1?y0:y1, - ny1 = y0<=y1?y1:y0, - sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)), - sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)), - sy0 = 16 + ny0, - sy1 = 16 + ny1; - if (y0>=0 && y1>=0) - visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU); - else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f). - draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU). - draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU); - } - if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) { - if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U); - const unsigned int - x = (unsigned int)cimg::round((mouse_x - 16.f)*(siz - one)/(disp.width() - 32),1,one?0:-1); - const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1); - if (_spectrum>=7) - cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx, - (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2), - (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3), - (double)(*this)(x,0,0,_spectrum - 1)); - else { - cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx); - cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); - cimg_sprintf(message._data + std::strlen(message),")"); - } - if (x0>=0 && x1>=0) { - const unsigned int - nx0 = (unsigned int)(x0<=x1?x0:x1), - nx1 = (unsigned int)(x0<=x1?x1:x0), - ny0 = (unsigned int)(y0<=y1?y0:y1), - ny1 = (unsigned int)(y0<=y1?y1:y0); - const double - cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), - cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), - cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32), - cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32); - if (y0>=0 && y1>=0) - cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", - x0,cx0,cy0,x1 + one,cx1,cy1); - else - cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", - x0,cx0,x1 + one,cx1); - } - text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3); - visu.draw_image((visu.width() - text.width())/2,1,~text); - } - visu.display(disp); - } - - // Test keys. - CImg<charT> filename(32); - switch (okey = key) { -#if cimg_OS!=2 - case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : -#endif - case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break; - case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), - CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). - _is_resized = true; - disp.set_key(key,false); okey = 0; - } break; - case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; - disp.set_key(key,false); okey = 0; - } break; - case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(CImgDisplay::screen_width()/2, - CImgDisplay::screen_height()/2,1),false)._is_resized = true; - disp.set_key(key,false); okey = 0; - } break; - case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; - disp.set_key(key,false); okey = 0; - } break; - case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - static unsigned int snap_number = 0; - if (visu || visu0) { - CImg<ucharT> &screen = visu?visu:visu0; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+screen).__draw_text(" Saving snapshot... ",false).display(disp); - screen.save(filename); - (+screen).__draw_text(" Snapshot '%s' saved. ",false,filename._data).display(disp); - } - disp.set_key(key,false); okey = 0; - } break; - case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - static unsigned int snap_number = 0; - if (visu || visu0) { - CImg<ucharT> &screen = visu?visu:visu0; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); -#endif - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+screen).__draw_text(" Saving instance... ",false).display(disp); - save(filename); - (+screen).__draw_text(" Instance '%s' saved. ",false,filename._data).display(disp); - } - disp.set_key(key,false); okey = 0; - } break; - } - - // Handle mouse motion and mouse buttons. - if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) { - visu.assign(); - if (disp.mouse_x()>=0 && disp.mouse_y()>=0) { - const int - mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32), - cx = cimg::cut(mx,0,(int)(siz - 1 - one)), - my = mouse_y - 16, - cy = cimg::cut(my,0,disp.height() - 32); - if (button&1) { - if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; } - } - else if (button&2) { - if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; } - } - else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; } - } else if (!button && obutton) selected = true; - obutton = button; omouse_x = mouse_x; omouse_y = mouse_y; - } - if (disp.is_resized()) { disp.resize(false); visu0.assign(); } - if (visu && visu0) disp.wait(); - if (!exit_on_anykey && okey && okey!=cimg::keyESC && - (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - disp.set_key(key,false); - okey = 0; - } - } - - disp._normalization = old_normalization; - if (x1>=0 && x1<x0) cimg::swap(x0,x1); - if (y1<y0) cimg::swap(y0,y1); - disp.set_key(okey); - return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1); - } - - //! Load image from a file. - /** - \param filename Filename, as a C-string. - \note The extension of \c filename defines the file format. If no filename - extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file. - **/ - CImg<T>& load(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load(): Specified filename is (null).", - cimg_instance); - - if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { - CImg<charT> filename_local(256); - load(cimg::load_network(filename,filename_local)); - std::remove(filename_local); - return *this; - } - - const char *const ext = cimg::split_filename(filename); - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - bool is_loaded = true; - try { -#ifdef cimg_load_plugin - cimg_load_plugin(filename); -#endif -#ifdef cimg_load_plugin1 - cimg_load_plugin1(filename); -#endif -#ifdef cimg_load_plugin2 - cimg_load_plugin2(filename); -#endif -#ifdef cimg_load_plugin3 - cimg_load_plugin3(filename); -#endif -#ifdef cimg_load_plugin4 - cimg_load_plugin4(filename); -#endif -#ifdef cimg_load_plugin5 - cimg_load_plugin5(filename); -#endif -#ifdef cimg_load_plugin6 - cimg_load_plugin6(filename); -#endif -#ifdef cimg_load_plugin7 - cimg_load_plugin7(filename); -#endif -#ifdef cimg_load_plugin8 - cimg_load_plugin8(filename); -#endif - // Ascii formats - if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename); - else if (!cimg::strcasecmp(ext,"dlm") || - !cimg::strcasecmp(ext,"txt")) load_dlm(filename); - - // 2D binary formats - else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename); - else if (!cimg::strcasecmp(ext,"jpg") || - !cimg::strcasecmp(ext,"jpeg") || - !cimg::strcasecmp(ext,"jpe") || - !cimg::strcasecmp(ext,"jfif") || - !cimg::strcasecmp(ext,"jif")) load_jpeg(filename); - else if (!cimg::strcasecmp(ext,"png")) load_png(filename); - else if (!cimg::strcasecmp(ext,"ppm") || - !cimg::strcasecmp(ext,"pgm") || - !cimg::strcasecmp(ext,"pnm") || - !cimg::strcasecmp(ext,"pbm") || - !cimg::strcasecmp(ext,"pnk")) load_pnm(filename); - else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename); - else if (!cimg::strcasecmp(ext,"tif") || - !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); - else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename); - else if (!cimg::strcasecmp(ext,"cr2") || - !cimg::strcasecmp(ext,"crw") || - !cimg::strcasecmp(ext,"dcr") || - !cimg::strcasecmp(ext,"mrw") || - !cimg::strcasecmp(ext,"nef") || - !cimg::strcasecmp(ext,"orf") || - !cimg::strcasecmp(ext,"pix") || - !cimg::strcasecmp(ext,"ptx") || - !cimg::strcasecmp(ext,"raf") || - !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename); - else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); - - // 3D binary formats - else if (!cimg::strcasecmp(ext,"dcm") || - !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename); - else if (!cimg::strcasecmp(ext,"hdr") || - !cimg::strcasecmp(ext,"nii")) load_analyze(filename); - else if (!cimg::strcasecmp(ext,"par") || - !cimg::strcasecmp(ext,"rec")) load_parrec(filename); - else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename); - else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename); - else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename); - else if (!cimg::strcasecmp(ext,"cimg") || - !cimg::strcasecmp(ext,"cimgz") || - !*ext) return load_cimg(filename); - - // Archive files - else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); - - // Image sequences - else if (!cimg::strcasecmp(ext,"avi") || - !cimg::strcasecmp(ext,"mov") || - !cimg::strcasecmp(ext,"asf") || - !cimg::strcasecmp(ext,"divx") || - !cimg::strcasecmp(ext,"flv") || - !cimg::strcasecmp(ext,"mpg") || - !cimg::strcasecmp(ext,"m1v") || - !cimg::strcasecmp(ext,"m2v") || - !cimg::strcasecmp(ext,"m4v") || - !cimg::strcasecmp(ext,"mjp") || - !cimg::strcasecmp(ext,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !cimg::strcasecmp(ext,"qt") || - !cimg::strcasecmp(ext,"rm") || - !cimg::strcasecmp(ext,"vob") || - !cimg::strcasecmp(ext,"wmv") || - !cimg::strcasecmp(ext,"xvid") || - !cimg::strcasecmp(ext,"mpeg")) load_video(filename); - else is_loaded = false; - } catch (CImgIOException&) { is_loaded = false; } - - // If nothing loaded, try to guess file format from magic number in file. - if (!is_loaded) { - std::FILE *file = cimg::std_fopen(filename,"rb"); - if (!file) { - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load(): Failed to open file '%s'.", - cimg_instance, - filename); - } - - const char *const f_type = cimg::ftype(file,filename); - cimg::fclose(file); - is_loaded = true; - try { - if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename); - else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename); - else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename); - else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename); - else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename); - else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename); - else if (!cimg::strcasecmp(f_type,"png")) load_png(filename); - else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); - else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); - else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename); - else is_loaded = false; - } catch (CImgIOException&) { is_loaded = false; } - } - - // If nothing loaded, try to load file with other means. - if (!is_loaded) { - try { - load_other(filename); - } catch (CImgIOException&) { - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load(): Failed to recognize format of file '%s'.", - cimg_instance, - filename); - } - } - cimg::exception_mode(omode); - return *this; - } - - //! Load image from a file \newinstance. - static CImg<T> get_load(const char *const filename) { - return CImg<T>().load(filename); - } - - //! Load image from an Ascii file. - /** - \param filename Filename, as a C -string. - **/ - CImg<T>& load_ascii(const char *const filename) { - return _load_ascii(0,filename); - } - - //! Load image from an Ascii file \inplace. - static CImg<T> get_load_ascii(const char *const filename) { - return CImg<T>().load_ascii(filename); - } - - //! Load image from an Ascii file \overloading. - CImg<T>& load_ascii(std::FILE *const file) { - return _load_ascii(file,0); - } - - //! Loadimage from an Ascii file \newinstance. - static CImg<T> get_load_ascii(std::FILE *const file) { - return CImg<T>().load_ascii(file); - } - - CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_ascii(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - CImg<charT> line(256); *line = 0; - int err = std::fscanf(nfile,"%255[^\n]",line._data); - unsigned int dx = 0, dy = 1, dz = 1, dc = 1; - cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); - err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]"); - if (!dx || !dy || !dz || !dc) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_ascii(): Invalid Ascii header in file '%s', image dimensions are set " - "to (%u,%u,%u,%u).", - cimg_instance, - filename?filename:"(FILE*)",dx,dy,dz,dc); - } - assign(dx,dy,dz,dc); - const ulongT siz = size(); - ulongT off = 0; - double val; - T *ptr = _data; - for (err = 1, off = 0; off<siz && err==1; ++off) { - err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val); - *(ptr++) = (T)val; - } - if (err!=1) - cimg::warn(_cimg_instance - "load_ascii(): Only %lu/%lu values read from file '%s'.", - cimg_instance, - off - 1,siz,filename?filename:"(FILE*)"); - - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a DLM file. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_dlm(const char *const filename) { - return _load_dlm(0,filename); - } - - //! Load image from a DLM file \newinstance. - static CImg<T> get_load_dlm(const char *const filename) { - return CImg<T>().load_dlm(filename); - } - - //! Load image from a DLM file \overloading. - CImg<T>& load_dlm(std::FILE *const file) { - return _load_dlm(file,0); - } - - //! Load image from a DLM file \newinstance. - static CImg<T> get_load_dlm(std::FILE *const file) { - return CImg<T>().load_dlm(file); - } - - CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_dlm(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); - CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0; - unsigned int cdx = 0, dx = 0, dy = 0; - int err = 0; - double val; - assign(256,256,1,1,(T)0); - while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) { - if (err>0) (*this)(cdx++,dy) = (T)val; - if (cdx>=_width) resize(3*_width/2,_height,1,1,0); - char c = 0; - if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') { - dx = std::max(cdx,dx); - if (++dy>=_height) resize(_width,3*_height/2,1,1,0); - cdx = 0; - } - } - if (cdx && err==1) { dx = cdx; ++dy; } - if (!dx || !dy) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_dlm(): Invalid DLM file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - resize(dx,dy,1,1,0); - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a BMP file. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_bmp(const char *const filename) { - return _load_bmp(0,filename); - } - - //! Load image from a BMP file \newinstance. - static CImg<T> get_load_bmp(const char *const filename) { - return CImg<T>().load_bmp(filename); - } - - //! Load image from a BMP file \overloading. - CImg<T>& load_bmp(std::FILE *const file) { - return _load_bmp(file,0); - } - - //! Load image from a BMP file \newinstance. - static CImg<T> get_load_bmp(std::FILE *const file) { - return CImg<T>().load_bmp(file); - } - - CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_bmp(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - CImg<ucharT> header(54); - cimg::fread(header._data,54,nfile); - if (*header!='B' || header[1]!='M') { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_bmp(): Invalid BMP file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - - // Read header and pixel buffer - int - file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), - offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), - header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24), - dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), - dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), - compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), - nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), - bpp = header[0x1C] + (header[0x1D]<<8); - - if (!file_size || file_size==offset) { - cimg::fseek(nfile,0,SEEK_END); - file_size = (int)cimg::ftell(nfile); - cimg::fseek(nfile,54,SEEK_SET); - } - if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR); - - const int - dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)), - align_bytes = (4 - dx_bytes%4)%4; - const ulongT - cimg_iobuffer = (ulongT)24*1024*1024, - buf_size = std::min((ulongT)cimg::abs(dy)*(dx_bytes + align_bytes),(ulongT)file_size - offset); - - CImg<intT> colormap; - if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0; - if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); } - const int xoffset = offset - 14 - header_size - 4*nb_colors; - if (xoffset>0) cimg::fseek(nfile,xoffset,SEEK_CUR); - - CImg<ucharT> buffer; - if (buf_size<cimg_iobuffer) { - buffer.assign(cimg::abs(dy)*(dx_bytes + align_bytes),1,1,1,0); - cimg::fread(buffer._data,buf_size,nfile); - } else buffer.assign(dx_bytes + align_bytes); - unsigned char *ptrs = buffer; - - // Decompress buffer (if necessary) - if (compression) { - if (file) - throw CImgIOException(_cimg_instance - "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.", - cimg_instance); - else { - if (!file) cimg::fclose(nfile); - return load_other(filename); - } - } - - // Read pixel data - assign(dx,cimg::abs(dy),1,3,0); - switch (bpp) { - case 1 : { // Monochrome - if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { - if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; - cimg::fseek(nfile,align_bytes,SEEK_CUR); - } - unsigned char mask = 0x80, val = 0; - cimg_forX(*this,x) { - if (mask==0x80) val = *(ptrs++); - const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0)); - (*this)(x,y,2) = (T)*(col++); - (*this)(x,y,1) = (T)*(col++); - (*this)(x,y,0) = (T)*(col++); - mask = cimg::ror(mask); - } - ptrs+=align_bytes; - } - } break; - case 4 : { // 16 colors - if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { - if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; - cimg::fseek(nfile,align_bytes,SEEK_CUR); - } - unsigned char mask = 0xF0, val = 0; - cimg_forX(*this,x) { - if (mask==0xF0) val = *(ptrs++); - const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4)); - const unsigned char *col = (unsigned char*)(colormap._data + color); - (*this)(x,y,2) = (T)*(col++); - (*this)(x,y,1) = (T)*(col++); - (*this)(x,y,0) = (T)*(col++); - mask = cimg::ror(mask,4); - } - ptrs+=align_bytes; - } - } break; - case 8 : { // 256 colors - if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { - if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; - cimg::fseek(nfile,align_bytes,SEEK_CUR); - } - cimg_forX(*this,x) { - const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++)); - (*this)(x,y,2) = (T)*(col++); - (*this)(x,y,1) = (T)*(col++); - (*this)(x,y,0) = (T)*(col++); - } - ptrs+=align_bytes; - } - } break; - case 16 : { // 16 bits colors - for (int y = height() - 1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { - if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; - cimg::fseek(nfile,align_bytes,SEEK_CUR); - } - cimg_forX(*this,x) { - const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); - const unsigned short col = (unsigned short)(c1|(c2<<8)); - (*this)(x,y,2) = (T)(col&0x1F); - (*this)(x,y,1) = (T)((col>>5)&0x1F); - (*this)(x,y,0) = (T)((col>>10)&0x1F); - } - ptrs+=align_bytes; - } - } break; - case 24 : { // 24 bits colors - for (int y = height() - 1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { - if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; - cimg::fseek(nfile,align_bytes,SEEK_CUR); - } - cimg_forX(*this,x) { - (*this)(x,y,2) = (T)*(ptrs++); - (*this)(x,y,1) = (T)*(ptrs++); - (*this)(x,y,0) = (T)*(ptrs++); - } - ptrs+=align_bytes; - } - } break; - case 32 : { // 32 bits colors - for (int y = height() - 1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { - if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; - cimg::fseek(nfile,align_bytes,SEEK_CUR); - } - cimg_forX(*this,x) { - (*this)(x,y,2) = (T)*(ptrs++); - (*this)(x,y,1) = (T)*(ptrs++); - (*this)(x,y,0) = (T)*(ptrs++); - ++ptrs; - } - ptrs+=align_bytes; - } - } break; - } - if (dy<0) mirror('y'); - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a JPEG file. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_jpeg(const char *const filename) { - return _load_jpeg(0,filename); - } - - //! Load image from a JPEG file \newinstance. - static CImg<T> get_load_jpeg(const char *const filename) { - return CImg<T>().load_jpeg(filename); - } - - //! Load image from a JPEG file \overloading. - CImg<T>& load_jpeg(std::FILE *const file) { - return _load_jpeg(file,0); - } - - //! Load image from a JPEG file \newinstance. - static CImg<T> get_load_jpeg(std::FILE *const file) { - return CImg<T>().load_jpeg(file); - } - - // Custom error handler for libjpeg. -#ifdef cimg_use_jpeg - struct _cimg_error_mgr { - struct jpeg_error_mgr original; - jmp_buf setjmp_buffer; - char message[JMSG_LENGTH_MAX]; - }; - - typedef struct _cimg_error_mgr *_cimg_error_ptr; - - METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) { - _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point - (*cinfo->err->format_message)(cinfo,c_err->message); - jpeg_destroy(cinfo); // Clean memory and temp files - longjmp(c_err->setjmp_buffer,1); - } -#endif - - CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_jpeg(): Specified filename is (null).", - cimg_instance); - -#ifndef cimg_use_jpeg - if (file) - throw CImgIOException(_cimg_instance - "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.", - cimg_instance); - else return load_other(filename); -#else - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - struct jpeg_decompress_struct cinfo; - struct _cimg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr.original); - jerr.original.error_exit = _cimg_jpeg_error_exit; - if (setjmp(jerr.setjmp_buffer)) { // JPEG error - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_jpeg(): Error message returned by libjpeg: %s.", - cimg_instance,jerr.message); - } - - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo,nfile); - jpeg_read_header(&cinfo,TRUE); - jpeg_start_decompress(&cinfo); - - if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { - if (!file) { - cimg::fclose(nfile); - return load_other(filename); - } else - throw CImgIOException(_cimg_instance - "load_jpeg(): Failed to load JPEG data from file '%s'.", - cimg_instance,filename?filename:"(FILE*)"); - } - CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components); - JSAMPROW row_pointer[1]; - try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); } - catch (...) { if (!file) cimg::fclose(nfile); throw; } - T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height, - *ptr_a = _data + 3UL*_width*_height; - while (cinfo.output_scanline<cinfo.output_height) { - *row_pointer = buffer._data; - if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) { - cimg::warn(_cimg_instance - "load_jpeg(): Incomplete data in file '%s'.", - cimg_instance,filename?filename:"(FILE*)"); - break; - } - const unsigned char *ptrs = buffer._data; - switch (_spectrum) { - case 1 : { - cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++); - } break; - case 3 : { - cimg_forX(*this,x) { - *(ptr_r++) = (T)*(ptrs++); - *(ptr_g++) = (T)*(ptrs++); - *(ptr_b++) = (T)*(ptrs++); - } - } break; - case 4 : { - cimg_forX(*this,x) { - *(ptr_r++) = (T)*(ptrs++); - *(ptr_g++) = (T)*(ptrs++); - *(ptr_b++) = (T)*(ptrs++); - *(ptr_a++) = (T)*(ptrs++); - } - } break; - } - } - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - if (!file) cimg::fclose(nfile); - return *this; -#endif - } - - //! Load image from a file, using Magick++ library. - /** - \param filename Filename, as a C-string. - **/ - // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de> - // This is experimental code, not much tested, use with care. - CImg<T>& load_magick(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_magick(): Specified filename is (null).", - cimg_instance); -#ifdef cimg_use_magick - Magick::Image image(filename); - const unsigned int W = image.size().width(), H = image.size().height(); - switch (image.type()) { - case Magick::PaletteMatteType : - case Magick::TrueColorMatteType : - case Magick::ColorSeparationType : { - assign(W,H,1,4); - T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); - Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); - for (ulongT off = (ulongT)W*H; off; --off) { - *(ptr_r++) = (T)(pixels->red); - *(ptr_g++) = (T)(pixels->green); - *(ptr_b++) = (T)(pixels->blue); - *(ptr_a++) = (T)(pixels->opacity); - ++pixels; - } - } break; - case Magick::PaletteType : - case Magick::TrueColorType : { - assign(W,H,1,3); - T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); - for (ulongT off = (ulongT)W*H; off; --off) { - *(ptr_r++) = (T)(pixels->red); - *(ptr_g++) = (T)(pixels->green); - *(ptr_b++) = (T)(pixels->blue); - ++pixels; - } - } break; - case Magick::GrayscaleMatteType : { - assign(W,H,1,2); - T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1); - Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); - for (ulongT off = (ulongT)W*H; off; --off) { - *(ptr_r++) = (T)(pixels->red); - *(ptr_a++) = (T)(pixels->opacity); - ++pixels; - } - } break; - default : { - assign(W,H,1,1); - T *ptr_r = data(0,0,0,0); - Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); - for (ulongT off = (ulongT)W*H; off; --off) { - *(ptr_r++) = (T)(pixels->red); - ++pixels; - } - } - } - return *this; -#else - throw CImgIOException(_cimg_instance - "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.", - cimg_instance, - filename); -#endif - } - - //! Load image from a file, using Magick++ library \newinstance. - static CImg<T> get_load_magick(const char *const filename) { - return CImg<T>().load_magick(filename); - } - - //! Load image from a PNG file. - /** - \param filename Filename, as a C-string. - \param[out] bits_per_pixel Number of bits per pixels used to store pixel values in the image file. - **/ - CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_pixel=0) { - return _load_png(0,filename,bits_per_pixel); - } - - //! Load image from a PNG file \newinstance. - static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_pixel=0) { - return CImg<T>().load_png(filename,bits_per_pixel); - } - - //! Load image from a PNG file \overloading. - CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) { - return _load_png(file,0,bits_per_pixel); - } - - //! Load image from a PNG file \newinstance. - static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) { - return CImg<T>().load_png(file,bits_per_pixel); - } - - // (Note: Most of this function has been written by Eric Fausett) - CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_pixel) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_png(): Specified filename is (null).", - cimg_instance); - -#ifndef cimg_use_png - cimg::unused(bits_per_pixel); - if (file) - throw CImgIOException(_cimg_instance - "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.", - cimg_instance); - - else return load_other(filename); -#else - // Open file and check for PNG validity -#if defined __GNUC__ - const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning - std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb"); -#else - const char *nfilename = filename; - std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb"); -#endif - unsigned char pngCheck[8] = { 0 }; - cimg::fread(pngCheck,8,(std::FILE*)nfile); - if (png_sig_cmp(pngCheck,0,8)) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_png(): Invalid PNG file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - - // Setup PNG structures for read - png_voidp user_error_ptr = 0; - png_error_ptr user_error_fn = 0, user_warning_fn = 0; - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn); - if (!png_ptr) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - if (!file) cimg::fclose(nfile); - png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0); - throw CImgIOException(_cimg_instance - "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - png_infop end_info = png_create_info_struct(png_ptr); - if (!end_info) { - if (!file) cimg::fclose(nfile); - png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0); - throw CImgIOException(_cimg_instance - "load_png(): Failed to initialize 'end_info' structure for file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - - // Error handling callback for png file reading - if (setjmp(png_jmpbuf(png_ptr))) { - if (!file) cimg::fclose((std::FILE*)nfile); - png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); - throw CImgIOException(_cimg_instance - "load_png(): Encountered unknown fatal error in libpng for file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - png_init_io(png_ptr, nfile); - png_set_sig_bytes(png_ptr, 8); - - // Get PNG Header Info up to data block - png_read_info(png_ptr,info_ptr); - png_uint_32 W, H; - int bit_depth, color_type, interlace_type; - bool is_gray = false; - png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0); - if (bits_per_pixel) *bits_per_pixel = (unsigned int)bit_depth; - - // Transforms to unify image data - if (color_type==PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png_ptr); - color_type = PNG_COLOR_TYPE_RGB; - bit_depth = 8; - } - if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) { - png_set_expand_gray_1_2_4_to_8(png_ptr); - is_gray = true; - bit_depth = 8; - } - if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) { - png_set_tRNS_to_alpha(png_ptr); - color_type |= PNG_COLOR_MASK_ALPHA; - } - if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) { - png_set_gray_to_rgb(png_ptr); - color_type |= PNG_COLOR_MASK_COLOR; - is_gray = true; - } - if (color_type==PNG_COLOR_TYPE_RGB) - png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER); - - png_read_update_info(png_ptr,info_ptr); - if (bit_depth!=8 && bit_depth!=16) { - if (!file) cimg::fclose(nfile); - png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0); - throw CImgIOException(_cimg_instance - "load_png(): Invalid bit depth %u in file '%s'.", - cimg_instance, - bit_depth,nfilename?nfilename:"(FILE*)"); - } - const int byte_depth = bit_depth>>3; - - // Allocate memory for image reading - png_bytep *const imgData = new png_bytep[H]; - for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W]; - png_read_image(png_ptr,imgData); - png_read_end(png_ptr,end_info); - - // Read pixel data - if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) { - if (!file) cimg::fclose(nfile); - png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0); - throw CImgIOException(_cimg_instance - "load_png(): Invalid color coding type %u in file '%s'.", - cimg_instance, - color_type,nfilename?nfilename:"(FILE*)"); - } - const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA); - try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); } - catch (...) { if (!file) cimg::fclose(nfile); throw; } - T - *ptr_r = data(0,0,0,0), - *ptr_g = is_gray?0:data(0,0,0,1), - *ptr_b = is_gray?0:data(0,0,0,2), - *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3); - switch (bit_depth) { - case 8 : { - cimg_forY(*this,y) { - const unsigned char *ptrs = (unsigned char*)imgData[y]; - cimg_forX(*this,x) { - *(ptr_r++) = (T)*(ptrs++); - if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs; - if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs; - if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs; - } - } - } break; - case 16 : { - cimg_forY(*this,y) { - const unsigned short *ptrs = (unsigned short*)(imgData[y]); - if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width); - cimg_forX(*this,x) { - *(ptr_r++) = (T)*(ptrs++); - if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs; - if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs; - if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs; - } - } - } break; - } - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - - // Deallocate image read memory - cimg_forY(*this,n) delete[] imgData[n]; - delete[] imgData; - if (!file) cimg::fclose(nfile); - return *this; -#endif - } - - //! Load image from a PNM file. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_pnm(const char *const filename) { - return _load_pnm(0,filename); - } - - //! Load image from a PNM file \newinstance. - static CImg<T> get_load_pnm(const char *const filename) { - return CImg<T>().load_pnm(filename); - } - - //! Load image from a PNM file \overloading. - CImg<T>& load_pnm(std::FILE *const file) { - return _load_pnm(file,0); - } - - //! Load image from a PNM file \newinstance. - static CImg<T> get_load_pnm(std::FILE *const file) { - return CImg<T>().load_pnm(file); - } - - CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_pnm(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - unsigned int ppm_type, W, H, D = 1, colormax = 255; - CImg<charT> item(16384,1,1,1,0); - int err, rval, gval, bval; - const longT cimg_iobuffer = (longT)24*1024*1024; - while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (cimg_sscanf(item," P%u",&ppm_type)!=1) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pnm(): PNM header not found in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - if (ppm_type!=1 && ppm_type!=4) { - if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) { - while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (cimg_sscanf(item,"%u",&colormax)!=1) - cimg::warn(_cimg_instance - "load_pnm(): COLORMAX field is undefined in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } else { colormax = D; D = 1; } - } - std::fgetc(nfile); - - switch (ppm_type) { - case 1 : { // 2D b&w Ascii - assign(W,H,1,1); - T* ptrd = _data; - cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; } - } break; - case 2 : { // 2D grey Ascii - assign(W,H,1,1); - T* ptrd = _data; - cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; } - } break; - case 3 : { // 2D color Ascii - assign(W,H,1,3); - T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - cimg_forXY(*this,x,y) { - if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { - *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; - } else break; - } - } break; - case 4 : { // 2D b&w binary (support 3D PINK extension) - CImg<ucharT> raw; - assign(W,H,D,1); - T *ptrd = data(0,0,0,0); - unsigned int w = 0, h = 0, d = 0; - for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer)); - cimg::fread(raw._data,raw._width,nfile); - to_read-=raw._width; - const unsigned char *ptrs = raw._data; - unsigned char mask = 0, val = 0; - for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) { - if (!mask) { if (off--) val = *(ptrs++); mask = 128; } - *(ptrd++) = (T)((val&mask)?0:255); - if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }} - } - } - } break; - case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension) - if (colormax<256) { // 8 bits - CImg<ucharT> raw; - assign(W,H,D,1); - T *ptrd = data(0,0,0,0); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer)); - cimg::fread(raw._data,raw._width,nfile); - to_read-=raw._width; - const unsigned char *ptrs = raw._data; - for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); - } - } else { // 16 bits - CImg<ushortT> raw; - assign(W,H,D,1); - T *ptrd = data(0,0,0,0); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer/2)); - cimg::fread(raw._data,raw._width,nfile); - if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); - to_read-=raw._width; - const unsigned short *ptrs = raw._data; - for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); - } - } - } break; - case 6 : { // 2D color binary - if (colormax<256) { // 8 bits - CImg<ucharT> raw; - assign(W,H,1,3); - T - *ptr_r = data(0,0,0,0), - *ptr_g = data(0,0,0,1), - *ptr_b = data(0,0,0,2); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer)); - cimg::fread(raw._data,raw._width,nfile); - to_read-=raw._width; - const unsigned char *ptrs = raw._data; - for (ulongT off = (ulongT)raw._width/3; off; --off) { - *(ptr_r++) = (T)*(ptrs++); - *(ptr_g++) = (T)*(ptrs++); - *(ptr_b++) = (T)*(ptrs++); - } - } - } else { // 16 bits - CImg<ushortT> raw; - assign(W,H,1,3); - T - *ptr_r = data(0,0,0,0), - *ptr_g = data(0,0,0,1), - *ptr_b = data(0,0,0,2); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer/2)); - cimg::fread(raw._data,raw._width,nfile); - if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); - to_read-=raw._width; - const unsigned short *ptrs = raw._data; - for (ulongT off = (ulongT)raw._width/3; off; --off) { - *(ptr_r++) = (T)*(ptrs++); - *(ptr_g++) = (T)*(ptrs++); - *(ptr_b++) = (T)*(ptrs++); - } - } - } - } break; - case 8 : { // 2D/3D grey binary with int32 integers (PINK extension) - CImg<intT> raw; - assign(W,H,D,1); - T *ptrd = data(0,0,0,0); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer)); - cimg::fread(raw._data,raw._width,nfile); - to_read-=raw._width; - const int *ptrs = raw._data; - for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); - } - } break; - case 9 : { // 2D/3D grey binary with float values (PINK extension) - CImg<floatT> raw; - assign(W,H,D,1); - T *ptrd = data(0,0,0,0); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer)); - cimg::fread(raw._data,raw._width,nfile); - to_read-=raw._width; - const float *ptrs = raw._data; - for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); - } - } break; - default : - assign(); - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pnm(): PNM type 'P%d' found, but type is not supported.", - cimg_instance, - filename?filename:"(FILE*)",ppm_type); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a PFM file. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_pfm(const char *const filename) { - return _load_pfm(0,filename); - } - - //! Load image from a PFM file \newinstance. - static CImg<T> get_load_pfm(const char *const filename) { - return CImg<T>().load_pfm(filename); - } - - //! Load image from a PFM file \overloading. - CImg<T>& load_pfm(std::FILE *const file) { - return _load_pfm(file,0); - } - - //! Load image from a PFM file \newinstance. - static CImg<T> get_load_pfm(std::FILE *const file) { - return CImg<T>().load_pfm(file); - } - - CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_pfm(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - char pfm_type; - CImg<charT> item(16384,1,1,1,0); - int W = 0, H = 0, err = 0; - double scale = 0; - while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (cimg_sscanf(item," P%c",&pfm_type)!=1) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pfm(): PFM header not found in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } else if (W<=0 || H<=0) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.", - cimg_instance,W,H, - filename?filename:"(FILE*)"); - } - if (err==2) { - while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (cimg_sscanf(item,"%lf",&scale)!=1) - cimg::warn(_cimg_instance - "load_pfm(): SCALE field is undefined in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - std::fgetc(nfile); - const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness(); - if (is_color) { - assign(W,H,1,3,(T)0); - CImg<floatT> buf(3*W); - T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - cimg_forY(*this,y) { - cimg::fread(buf._data,3*W,nfile); - if (is_inverted) cimg::invert_endianness(buf._data,3*W); - const float *ptrs = buf._data; - cimg_forX(*this,x) { - *(ptr_r++) = (T)*(ptrs++); - *(ptr_g++) = (T)*(ptrs++); - *(ptr_b++) = (T)*(ptrs++); - } - } - } else { - assign(W,H,1,1,(T)0); - CImg<floatT> buf(W); - T *ptrd = data(0,0,0,0); - cimg_forY(*this,y) { - cimg::fread(buf._data,W,nfile); - if (is_inverted) cimg::invert_endianness(buf._data,W); - const float *ptrs = buf._data; - cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++); - } - } - if (!file) cimg::fclose(nfile); - return mirror('y'); // Most of the .pfm files are flipped along the y-axis - } - - //! Load image from a RGB file. - /** - \param filename Filename, as a C-string. - \param dimw Width of the image buffer. - \param dimh Height of the image buffer. - **/ - CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { - return _load_rgb(0,filename,dimw,dimh); - } - - //! Load image from a RGB file \newinstance. - static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { - return CImg<T>().load_rgb(filename,dimw,dimh); - } - - //! Load image from a RGB file \overloading. - CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { - return _load_rgb(file,0,dimw,dimh); - } - - //! Load image from a RGB file \newinstance. - static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { - return CImg<T>().load_rgb(file,dimw,dimh); - } - - CImg<T>& _load_rgb(std::FILE *const file, const char *const filename, - const unsigned int dimw, const unsigned int dimh) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_rgb(): Specified filename is (null).", - cimg_instance); - - if (!dimw || !dimh) return assign(); - const longT cimg_iobuffer = (longT)24*1024*1024; - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - CImg<ucharT> raw; - assign(dimw,dimh,1,3); - T - *ptr_r = data(0,0,0,0), - *ptr_g = data(0,0,0,1), - *ptr_b = data(0,0,0,2); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer)); - cimg::fread(raw._data,raw._width,nfile); - to_read-=raw._width; - const unsigned char *ptrs = raw._data; - for (ulongT off = raw._width/3UL; off; --off) { - *(ptr_r++) = (T)*(ptrs++); - *(ptr_g++) = (T)*(ptrs++); - *(ptr_b++) = (T)*(ptrs++); - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a RGBA file. - /** - \param filename Filename, as a C-string. - \param dimw Width of the image buffer. - \param dimh Height of the image buffer. - **/ - CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { - return _load_rgba(0,filename,dimw,dimh); - } - - //! Load image from a RGBA file \newinstance. - static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { - return CImg<T>().load_rgba(filename,dimw,dimh); - } - - //! Load image from a RGBA file \overloading. - CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { - return _load_rgba(file,0,dimw,dimh); - } - - //! Load image from a RGBA file \newinstance. - static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { - return CImg<T>().load_rgba(file,dimw,dimh); - } - - CImg<T>& _load_rgba(std::FILE *const file, const char *const filename, - const unsigned int dimw, const unsigned int dimh) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_rgba(): Specified filename is (null).", - cimg_instance); - - if (!dimw || !dimh) return assign(); - const longT cimg_iobuffer = (longT)24*1024*1024; - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - CImg<ucharT> raw; - assign(dimw,dimh,1,4); - T - *ptr_r = data(0,0,0,0), - *ptr_g = data(0,0,0,1), - *ptr_b = data(0,0,0,2), - *ptr_a = data(0,0,0,3); - for (longT to_read = (longT)size(); to_read>0; ) { - raw.assign(std::min(to_read,cimg_iobuffer)); - cimg::fread(raw._data,raw._width,nfile); - to_read-=raw._width; - const unsigned char *ptrs = raw._data; - for (ulongT off = raw._width/4UL; off; --off) { - *(ptr_r++) = (T)*(ptrs++); - *(ptr_g++) = (T)*(ptrs++); - *(ptr_b++) = (T)*(ptrs++); - *(ptr_a++) = (T)*(ptrs++); - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a TIFF file. - /** - \param filename Filename, as a C-string. - \param first_frame First frame to read (for multi-pages tiff). - \param last_frame Last frame to read (for multi-pages tiff). - \param step_frame Step value of frame reading. - \param[out] voxel_size Voxel size, as stored in the filename. - \param[out] description Description, as stored in the filename. - \note - - libtiff support is enabled by defining the precompilation - directive \c cimg_use_tif. - - When libtiff is enabled, 2D and 3D (multipage) several - channel per pixel are supported for - <tt>char,uchar,short,ushort,float</tt> and \c double pixel types. - - If \c cimg_use_tif is not defined at compile time the - function uses CImg<T>& load_other(const char*). - **/ - CImg<T>& load_tiff(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg<charT> *const description=0) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_tiff(): Specified filename is (null).", - cimg_instance); - - const unsigned int - nfirst_frame = first_frame<last_frame?first_frame:last_frame, - nstep_frame = step_frame?step_frame:1; - unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame; - -#ifndef cimg_use_tiff - cimg::unused(voxel_size,description); - if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1) - throw CImgArgumentException(_cimg_instance - "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.", - cimg_instance, - filename); - return load_other(filename); -#else -#if cimg_verbosity<3 - TIFFSetWarningHandler(0); - TIFFSetErrorHandler(0); -#endif - TIFF *tif = TIFFOpen(filename,"r"); - if (tif) { - unsigned int nb_images = 0; - do ++nb_images; while (TIFFReadDirectory(tif)); - if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) - cimg::warn(_cimg_instance - "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).", - cimg_instance, - filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); - - if (nfirst_frame>=nb_images) return assign(); - if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; - TIFFSetDirectory(tif,0); - CImg<T> frame; - for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { - frame._load_tiff(tif,l,voxel_size,description); - if (l==nfirst_frame) - assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum); - if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum) - resize(std::max(frame._width,_width), - std::max(frame._height,_height),-100, - std::max(frame._spectrum,_spectrum),0); - draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame); - } - TIFFClose(tif); - } else throw CImgIOException(_cimg_instance - "load_tiff(): Failed to open file '%s'.", - cimg_instance, - filename); - return *this; -#endif - } - - //! Load image from a TIFF file \newinstance. - static CImg<T> get_load_tiff(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg<charT> *const description=0) { - return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); - } - - // (Original contribution by Jerome Boulanger). -#ifdef cimg_use_tiff - template<typename t> - void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, - const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { - t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); - if (buf) { - for (unsigned int row = 0; row<ny; row+=th) - for (unsigned int col = 0; col<nx; col+=tw) { - if (TIFFReadTile(tif,buf,col,row,0,0)<0) { - _TIFFfree(buf); TIFFClose(tif); - throw CImgIOException(_cimg_instance - "load_tiff(): Invalid tile in file '%s'.", - cimg_instance, - TIFFFileName(tif)); - } - const t *ptr = buf; - for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr) - for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc) - for (unsigned int vv = 0; vv<samplesperpixel; ++vv) - (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]); - } - _TIFFfree(buf); - } - } - - template<typename t> - void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, - const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { - t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); - if (buf) { - for (unsigned int vv = 0; vv<samplesperpixel; ++vv) - for (unsigned int row = 0; row<ny; row+=th) - for (unsigned int col = 0; col<nx; col+=tw) { - if (TIFFReadTile(tif,buf,col,row,0,vv)<0) { - _TIFFfree(buf); TIFFClose(tif); - throw CImgIOException(_cimg_instance - "load_tiff(): Invalid tile in file '%s'.", - cimg_instance, - TIFFFileName(tif)); - } - const t *ptr = buf; - for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr) - for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc) - (*this)(cc,rr,vv) = (T)*(ptr++); - } - _TIFFfree(buf); - } - } - - template<typename t> - void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { - t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); - if (buf) { - uint32 row, rowsperstrip = (uint32)-1; - TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); - for (row = 0; row<ny; row+= rowsperstrip) { - uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip); - tstrip_t strip = TIFFComputeStrip(tif, row, 0); - if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { - _TIFFfree(buf); TIFFClose(tif); - throw CImgIOException(_cimg_instance - "load_tiff(): Invalid strip in file '%s'.", - cimg_instance, - TIFFFileName(tif)); - } - const t *ptr = buf; - for (unsigned int rr = 0; rr<nrow; ++rr) - for (unsigned int cc = 0; cc<nx; ++cc) - for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++); - } - _TIFFfree(buf); - } - } - - template<typename t> - void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { - t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); - if (buf) { - uint32 row, rowsperstrip = (uint32)-1; - TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); - for (unsigned int vv = 0; vv<samplesperpixel; ++vv) - for (row = 0; row<ny; row+= rowsperstrip) { - uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip); - tstrip_t strip = TIFFComputeStrip(tif, row, vv); - if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { - _TIFFfree(buf); TIFFClose(tif); - throw CImgIOException(_cimg_instance - "load_tiff(): Invalid strip in file '%s'.", - cimg_instance, - TIFFFileName(tif)); - } - const t *ptr = buf; - for (unsigned int rr = 0;rr<nrow; ++rr) - for (unsigned int cc = 0; cc<nx; ++cc) - (*this)(cc,row + rr,vv) = (T)*(ptr++); - } - _TIFFfree(buf); - } - } - - CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory, - float *const voxel_size, CImg<charT> *const description) { - if (!TIFFSetDirectory(tif,directory)) return assign(); - uint16 samplesperpixel = 1, bitspersample = 8, photo = 0; - uint16 sampleformat = 1; - uint32 nx = 1, ny = 1; - const char *const filename = TIFFFileName(tif); - const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); - TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); - TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); - TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); - TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); - TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); - if (voxel_size) { - const char *s_description = 0; - float vx = 0, vy = 0, vz = 0; - if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) { - const char *s_desc = std::strstr(s_description,"VX="); - if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format - voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz; - } - s_desc = std::strstr(s_description,"spacing="); - if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format - voxel_size[2] = vz; - } - } - TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size); - TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1); - voxel_size[0] = 1.f/voxel_size[0]; - voxel_size[1] = 1.f/voxel_size[1]; - } - if (description) { - const char *s_description = 0; - if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) - CImg<charT>::string(s_description).move_to(*description); - } - const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel; - assign(nx,ny,1,spectrum); - - if ((photo>=3 && sampleformat==1 && - (bitspersample==4 || bitspersample==8) && - (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) || - (bitspersample==1 && samplesperpixel==1)) { - // Special case for unsigned color images. - uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32)); - if (!raster) { - _TIFFfree(raster); TIFFClose(tif); - throw CImgException(_cimg_instance - "load_tiff(): Failed to allocate memory (%s) for file '%s'.", - cimg_instance, - cimg::strbuffersize(nx*ny*sizeof(uint32)),filename); - } - TIFFReadRGBAImage(tif,nx,ny,raster,0); - switch (spectrum) { - case 1 : - cimg_forXY(*this,x,y) - (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); - break; - case 3 : - cimg_forXY(*this,x,y) { - (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); - (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 -y) + x]); - (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 -y) + x]); - } - break; - case 4 : - cimg_forXY(*this,x,y) { - (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); - (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); - (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); - (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]); - } - break; - } - _TIFFfree(raster); - } else { // Other cases - uint16 config; - TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); - if (TIFFIsTiled(tif)) { - uint32 tw = 1, th = 1; - TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); - TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); - if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { - case 8 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th); - break; - case 16 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th); - break; - case 32 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th); - else if (sampleformat==SAMPLEFORMAT_INT) - _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th); - break; - case 64 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th); - else if (sampleformat==SAMPLEFORMAT_INT) - _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th); - break; - } else switch (bitspersample) { - case 8 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th); - break; - case 16 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th); - break; - case 32 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th); - else if (sampleformat==SAMPLEFORMAT_INT) - _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th); - break; - case 64 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th); - else if (sampleformat==SAMPLEFORMAT_INT) - _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th); - else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th); - break; - } - } else { - if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { - case 8 : - if (sampleformat==SAMPLEFORMAT_UINT) - _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny); - else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny); - break; - case 16 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny); - else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny); - break; - case 32 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny); - else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny); - else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny); - break; - case 64 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny); - else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny); - else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny); - break; - } else switch (bitspersample) { - case 8 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny); - else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny); - break; - case 16 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny); - else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny); - break; - case 32 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny); - else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny); - else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny); - break; - case 64 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny); - else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny); - else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny); - break; - } - } - } - return *this; - } -#endif - - //! Load image from a MINC2 file. - /** - \param filename Filename, as a C-string. - **/ - // (Original code by Haz-Edine Assemlal). - CImg<T>& load_minc2(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_minc2(): Specified filename is (null).", - cimg_instance); -#ifndef cimg_use_minc2 - return load_other(filename); -#else - minc::minc_1_reader rdr; - rdr.open(filename); - assign(rdr.ndim(1)?rdr.ndim(1):1, - rdr.ndim(2)?rdr.ndim(2):1, - rdr.ndim(3)?rdr.ndim(3):1, - rdr.ndim(4)?rdr.ndim(4):1); - if (cimg::type<T>::string()==cimg::type<unsigned char>::string()) - rdr.setup_read_byte(); - else if (cimg::type<T>::string()==cimg::type<int>::string()) - rdr.setup_read_int(); - else if (cimg::type<T>::string()==cimg::type<double>::string()) - rdr.setup_read_double(); - else - rdr.setup_read_float(); - minc::load_standard_volume(rdr,this->_data); - return *this; -#endif - } - - //! Load image from a MINC2 file \newinstance. - static CImg<T> get_load_minc2(const char *const filename) { - return CImg<T>().load_analyze(filename); - } - - //! Load image from an ANALYZE7.5/NIFTI file. - /** - \param filename Filename, as a C-string. - \param[out] voxel_size Pointer to the three voxel sizes read from the file. - **/ - CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) { - return _load_analyze(0,filename,voxel_size); - } - - //! Load image from an ANALYZE7.5/NIFTI file \newinstance. - static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) { - return CImg<T>().load_analyze(filename,voxel_size); - } - - //! Load image from an ANALYZE7.5/NIFTI file \overloading. - CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) { - return _load_analyze(file,0,voxel_size); - } - - //! Load image from an ANALYZE7.5/NIFTI file \newinstance. - static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) { - return CImg<T>().load_analyze(file,voxel_size); - } - - CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_analyze(): Specified filename is (null).", - cimg_instance); - - std::FILE *nfile_header = 0, *nfile = 0; - if (!file) { - CImg<charT> body(1024); - const char *const ext = cimg::split_filename(filename,body); - if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file - nfile_header = cimg::fopen(filename,"rb"); - cimg_sprintf(body._data + std::strlen(body),".img"); - nfile = cimg::fopen(body,"rb"); - } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file - nfile = cimg::fopen(filename,"rb"); - cimg_sprintf(body._data + std::strlen(body),".hdr"); - nfile_header = cimg::fopen(body,"rb"); - } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file - } else nfile_header = nfile = file; // File is a Niftii file - if (!nfile || !nfile_header) - throw CImgIOException(_cimg_instance - "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - // Read header. - bool endian = false; - unsigned int header_size; - cimg::fread(&header_size,1,nfile_header); - if (!header_size) - throw CImgIOException(_cimg_instance - "load_analyze(): Invalid zero-size header in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); } - - unsigned char *const header = new unsigned char[header_size]; - cimg::fread(header + 4,header_size - 4,nfile_header); - if (!file && nfile_header!=nfile) cimg::fclose(nfile_header); - if (endian) { - cimg::invert_endianness((short*)(header + 40),5); - cimg::invert_endianness((short*)(header + 70),1); - cimg::invert_endianness((short*)(header + 72),1); - cimg::invert_endianness((float*)(header + 76),4); - cimg::invert_endianness((float*)(header + 108),1); - cimg::invert_endianness((float*)(header + 112),1); - } - - if (nfile_header==nfile) { - const unsigned int vox_offset = (unsigned int)*(float*)(header + 108); - std::fseek(nfile,vox_offset,SEEK_SET); - } - - unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1; - if (!dim[0]) - cimg::warn(_cimg_instance - "load_analyze(): File '%s' defines an image with zero dimensions.", - cimg_instance, - filename?filename:"(FILE*)"); - - if (dim[0]>4) - cimg::warn(_cimg_instance - "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.", - cimg_instance, - filename?filename:"(FILE*)",dim[0]); - - if (dim[0]>=1) dimx = dim[1]; - if (dim[0]>=2) dimy = dim[2]; - if (dim[0]>=3) dimz = dim[3]; - if (dim[0]>=4) dimv = dim[4]; - float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1; - const unsigned short datatype = *(unsigned short*)(header + 70); - if (voxel_size) { - const float *vsize = (float*)(header + 76); - voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3]; - } - delete[] header; - - // Read pixel data. - assign(dimx,dimy,dimz,dimv); - const size_t pdim = (size_t)dimx*dimy*dimz*dimv; - switch (datatype) { - case 2 : { - unsigned char *const buffer = new unsigned char[pdim]; - cimg::fread(buffer,pdim,nfile); - cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); - delete[] buffer; - } break; - case 4 : { - short *const buffer = new short[pdim]; - cimg::fread(buffer,pdim,nfile); - if (endian) cimg::invert_endianness(buffer,pdim); - cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); - delete[] buffer; - } break; - case 8 : { - int *const buffer = new int[pdim]; - cimg::fread(buffer,pdim,nfile); - if (endian) cimg::invert_endianness(buffer,pdim); - cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); - delete[] buffer; - } break; - case 16 : { - float *const buffer = new float[pdim]; - cimg::fread(buffer,pdim,nfile); - if (endian) cimg::invert_endianness(buffer,pdim); - cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); - delete[] buffer; - } break; - case 64 : { - double *const buffer = new double[pdim]; - cimg::fread(buffer,pdim,nfile); - if (endian) cimg::invert_endianness(buffer,pdim); - cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); - delete[] buffer; - } break; - default : - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_analyze(): Unable to load datatype %d in file '%s'", - cimg_instance, - datatype,filename?filename:"(FILE*)"); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a .cimg[z] file. - /** - \param filename Filename, as a C-string. - \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignment. - **/ - CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) { - CImgList<T> list; - list.load_cimg(filename); - if (list._width==1) return list[0].move_to(*this); - return assign(list.get_append(axis,align)); - } - - //! Load image from a .cimg[z] file \newinstance - static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) { - return CImg<T>().load_cimg(filename,axis,align); - } - - //! Load image from a .cimg[z] file \overloading. - CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) { - CImgList<T> list; - list.load_cimg(file); - if (list._width==1) return list[0].move_to(*this); - return assign(list.get_append(axis,align)); - } - - //! Load image from a .cimg[z] file \newinstance - static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) { - return CImg<T>().load_cimg(file,axis,align); - } - - //! Load sub-images of a .cimg file. - /** - \param filename Filename, as a C-string. - \param n0 Starting frame. - \param n1 Ending frame (~0U for max). - \param x0 X-coordinate of the starting sub-image vertex. - \param y0 Y-coordinate of the starting sub-image vertex. - \param z0 Z-coordinate of the starting sub-image vertex. - \param c0 C-coordinate of the starting sub-image vertex. - \param x1 X-coordinate of the ending sub-image vertex (~0U for max). - \param y1 Y-coordinate of the ending sub-image vertex (~0U for max). - \param z1 Z-coordinate of the ending sub-image vertex (~0U for max). - \param c1 C-coordinate of the ending sub-image vertex (~0U for max). - \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignment. - **/ - CImg<T>& load_cimg(const char *const filename, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1, - const char axis='z', const float align=0) { - CImgList<T> list; - list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); - if (list._width==1) return list[0].move_to(*this); - return assign(list.get_append(axis,align)); - } - - //! Load sub-images of a .cimg file \newinstance. - static CImg<T> get_load_cimg(const char *const filename, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1, - const char axis='z', const float align=0) { - return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); - } - - //! Load sub-images of a .cimg file \overloading. - CImg<T>& load_cimg(std::FILE *const file, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1, - const char axis='z', const float align=0) { - CImgList<T> list; - list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); - if (list._width==1) return list[0].move_to(*this); - return assign(list.get_append(axis,align)); - } - - //! Load sub-images of a .cimg file \newinstance. - static CImg<T> get_load_cimg(std::FILE *const file, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1, - const char axis='z', const float align=0) { - return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); - } - - //! Load image from an INRIMAGE-4 file. - /** - \param filename Filename, as a C-string. - \param[out] voxel_size Pointer to the three voxel sizes read from the file. - **/ - CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) { - return _load_inr(0,filename,voxel_size); - } - - //! Load image from an INRIMAGE-4 file \newinstance. - static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) { - return CImg<T>().load_inr(filename,voxel_size); - } - - //! Load image from an INRIMAGE-4 file \overloading. - CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) { - return _load_inr(file,0,voxel_size); - } - - //! Load image from an INRIMAGE-4 file \newinstance. - static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) { - return CImg<T>().load_inr(file,voxel_size); - } - - static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { - CImg<charT> item(1024), tmp1(64), tmp2(64); - *item = *tmp1 = *tmp2 = 0; - out[0] = std::fscanf(file,"%63s",item._data); - out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; - if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) - throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.", - pixel_type()); - - while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) { - cimg_sscanf(item," XDIM%*[^0-9]%d",out); - cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1); - cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2); - cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3); - cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6); - if (voxel_size) { - cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size); - cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1); - cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2); - } - if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1; - switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) { - case 0 : break; - case 2 : - out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; - std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough - case 1 : - if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; - if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; - if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; - if (out[4]>=0) break; // fallthrough - default : - throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", - pixel_type(), - tmp2._data); - } - } - if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) - throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.", - pixel_type(), - out[0],out[1],out[2],out[3]); - if (out[4]<0 || out[5]<0) - throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.", - pixel_type()); - if (out[6]<0) - throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.", - pixel_type()); - if (out[7]<0) - throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.", - pixel_type()); - } - - CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) { -#define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \ - if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ - Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \ - cimg_forYZ(*this,y,z) { \ - cimg::fread(val,fopt[0]*fopt[3],nfile); \ - if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \ - xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \ - } \ - delete[] val; \ - loaded = true; \ - } - - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_inr(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - int fopt[8], endian = cimg::endianness()?1:0; - bool loaded = false; - if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1; - _load_inr_header(nfile,fopt,voxel_size); - assign(fopt[0],fopt[1],fopt[2],fopt[3]); - _cimg_load_inr_case(0,0,8,unsigned char); - _cimg_load_inr_case(0,1,8,char); - _cimg_load_inr_case(0,0,16,unsigned short); - _cimg_load_inr_case(0,1,16,short); - _cimg_load_inr_case(0,0,32,unsigned int); - _cimg_load_inr_case(0,1,32,int); - _cimg_load_inr_case(1,0,32,float); - _cimg_load_inr_case(1,1,32,float); - _cimg_load_inr_case(1,0,64,double); - _cimg_load_inr_case(1,1,64,double); - if (!loaded) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_inr(): Unknown pixel type defined in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a EXR file. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_exr(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_exr(): Specified filename is (null).", - cimg_instance); -#if defined(cimg_use_openexr) - Imf::RgbaInputFile file(filename); - Imath::Box2i dw = file.dataWindow(); - const int - inwidth = dw.max.x - dw.min.x + 1, - inheight = dw.max.y - dw.min.y + 1; - Imf::Array2D<Imf::Rgba> pixels; - pixels.resizeErase(inheight,inwidth); - file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth); - file.readPixels(dw.min.y, dw.max.y); - assign(inwidth,inheight,1,4); - T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); - cimg_forXY(*this,x,y) { - *(ptr_r++) = (T)pixels[y][x].r; - *(ptr_g++) = (T)pixels[y][x].g; - *(ptr_b++) = (T)pixels[y][x].b; - *(ptr_a++) = (T)pixels[y][x].a; - } -#elif defined(cimg_use_tinyexr) - float *res; - const char *err = 0; - int width = 0, height = 0; - const int ret = LoadEXR(&res,&width,&height,filename,&err); - if (ret) throw CImgIOException(_cimg_instance - "load_exr(): Unable to load EXR file '%s'.", - cimg_instance,filename); - CImg<floatT>(out,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this); - std::free(res); -#else - return load_other(filename); -#endif - return *this; - } - - //! Load image from a EXR file \newinstance. - static CImg<T> get_load_exr(const char *const filename) { - return CImg<T>().load_exr(filename); - } - - //! Load image from a PANDORE-5 file. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_pandore(const char *const filename) { - return _load_pandore(0,filename); - } - - //! Load image from a PANDORE-5 file \newinstance. - static CImg<T> get_load_pandore(const char *const filename) { - return CImg<T>().load_pandore(filename); - } - - //! Load image from a PANDORE-5 file \overloading. - CImg<T>& load_pandore(std::FILE *const file) { - return _load_pandore(file,0); - } - - //! Load image from a PANDORE-5 file \newinstance. - static CImg<T> get_load_pandore(std::FILE *const file) { - return CImg<T>().load_pandore(file); - } - - CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) { -#define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \ - cimg::fread(dims,nbdim,nfile); \ - if (endian) cimg::invert_endianness(dims,nbdim); \ - assign(nwidth,nheight,ndepth,ndim); \ - const size_t siz = size(); \ - stype *buffer = new stype[siz]; \ - cimg::fread(buffer,siz,nfile); \ - if (endian) cimg::invert_endianness(buffer,siz); \ - T *ptrd = _data; \ - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \ - buffer-=siz; \ - delete[] buffer - -#define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \ - if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \ - else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \ - else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \ - else throw CImgIOException(_cimg_instance \ - "load_pandore(): Unknown pixel datatype in file '%s'.", \ - cimg_instance, \ - filename?filename:"(FILE*)"); } - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_pandore(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - CImg<charT> header(32); - cimg::fread(header._data,12,nfile); - if (cimg::strncasecmp("PANDORE",header,7)) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pandore(): PANDORE header not found in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - unsigned int imageid, dims[8] = { 0 }; - int ptbuf[4] = { 0 }; - cimg::fread(&imageid,1,nfile); - const bool endian = imageid>255; - if (endian) cimg::invert_endianness(imageid); - cimg::fread(header._data,20,nfile); - - switch (imageid) { - case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break; - case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break; - case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break; - case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break; - case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break; - case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break; - case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break; - case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break; - case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break; - case 11 : { // Region 1D - cimg::fread(dims,3,nfile); - if (endian) cimg::invert_endianness(dims,3); - assign(dims[1],1,1,1); - const unsigned siz = size(); - if (dims[2]<256) { - unsigned char *buffer = new unsigned char[siz]; - cimg::fread(buffer,siz,nfile); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } else { - if (dims[2]<65536) { - unsigned short *buffer = new unsigned short[siz]; - cimg::fread(buffer,siz,nfile); - if (endian) cimg::invert_endianness(buffer,siz); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } else { - unsigned int *buffer = new unsigned int[siz]; - cimg::fread(buffer,siz,nfile); - if (endian) cimg::invert_endianness(buffer,siz); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } - } - } - break; - case 12 : { // Region 2D - cimg::fread(dims,4,nfile); - if (endian) cimg::invert_endianness(dims,4); - assign(dims[2],dims[1],1,1); - const size_t siz = size(); - if (dims[3]<256) { - unsigned char *buffer = new unsigned char[siz]; - cimg::fread(buffer,siz,nfile); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } else { - if (dims[3]<65536) { - unsigned short *buffer = new unsigned short[siz]; - cimg::fread(buffer,siz,nfile); - if (endian) cimg::invert_endianness(buffer,siz); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } else { - unsigned int *buffer = new unsigned int[siz]; - cimg::fread(buffer,siz,nfile); - if (endian) cimg::invert_endianness(buffer,siz); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } - } - } - break; - case 13 : { // Region 3D - cimg::fread(dims,5,nfile); - if (endian) cimg::invert_endianness(dims,5); - assign(dims[3],dims[2],dims[1],1); - const size_t siz = size(); - if (dims[4]<256) { - unsigned char *buffer = new unsigned char[siz]; - cimg::fread(buffer,siz,nfile); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } else { - if (dims[4]<65536) { - unsigned short *buffer = new unsigned short[siz]; - cimg::fread(buffer,siz,nfile); - if (endian) cimg::invert_endianness(buffer,siz); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } else { - unsigned int *buffer = new unsigned int[siz]; - cimg::fread(buffer,siz,nfile); - if (endian) cimg::invert_endianness(buffer,siz); - T *ptrd = _data; - cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); - buffer-=siz; - delete[] buffer; - } - } - } - break; - case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break; - case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break; - case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break; - case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break; - case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break; - case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break; - case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break; - case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break; - case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break; - case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break; - case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break; - case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break; - case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break; - case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break; - case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); - break; - case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break; - case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); - break; - case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break; - case 34 : { // Points 1D - cimg::fread(ptbuf,1,nfile); - if (endian) cimg::invert_endianness(ptbuf,1); - assign(1); (*this)(0) = (T)ptbuf[0]; - } break; - case 35 : { // Points 2D - cimg::fread(ptbuf,2,nfile); - if (endian) cimg::invert_endianness(ptbuf,2); - assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; - } break; - case 36 : { // Points 3D - cimg::fread(ptbuf,3,nfile); - if (endian) cimg::invert_endianness(ptbuf,3); - assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; - } break; - default : - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_pandore(): Unable to load data with ID_type %u in file '%s'.", - cimg_instance, - imageid,filename?filename:"(FILE*)"); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image from a PAR-REC (Philips) file. - /** - \param filename Filename, as a C-string. - \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignment. - **/ - CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) { - CImgList<T> list; - list.load_parrec(filename); - if (list._width==1) return list[0].move_to(*this); - return assign(list.get_append(axis,align)); - } - - //! Load image from a PAR-REC (Philips) file \newinstance. - static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) { - return CImg<T>().load_parrec(filename,axis,align); - } - - //! Load image from a raw binary file. - /** - \param filename Filename, as a C-string. - \param size_x Width of the image buffer. - \param size_y Height of the image buffer. - \param size_z Depth of the image buffer. - \param size_c Spectrum of the image buffer. - \param is_multiplexed Tells if the image values are multiplexed along the C-axis. - \param invert_endianness Tells if the endianness of the image buffer must be inverted. - \param offset Starting offset of the read in the specified file. - **/ - CImg<T>& load_raw(const char *const filename, - const unsigned int size_x=0, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1, - const bool is_multiplexed=false, const bool invert_endianness=false, - const ulongT offset=0) { - return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - //! Load image from a raw binary file \newinstance. - static CImg<T> get_load_raw(const char *const filename, - const unsigned int size_x=0, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1, - const bool is_multiplexed=false, const bool invert_endianness=false, - const ulongT offset=0) { - return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - //! Load image from a raw binary file \overloading. - CImg<T>& load_raw(std::FILE *const file, - const unsigned int size_x=0, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1, - const bool is_multiplexed=false, const bool invert_endianness=false, - const ulongT offset=0) { - return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - //! Load image from a raw binary file \newinstance. - static CImg<T> get_load_raw(std::FILE *const file, - const unsigned int size_x=0, const unsigned int size_y=1, - const unsigned int size_z=1, const unsigned int size_c=1, - const bool is_multiplexed=false, const bool invert_endianness=false, - const ulongT offset=0) { - return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); - } - - CImg<T>& _load_raw(std::FILE *const file, const char *const filename, - const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, - const bool is_multiplexed, const bool invert_endianness, - const ulongT offset) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_raw(): Specified filename is (null).", - cimg_instance); - if (cimg::is_directory(filename)) - throw CImgArgumentException(_cimg_instance - "load_raw(): Specified filename '%s' is a directory.", - cimg_instance,filename); - - ulongT siz = (ulongT)size_x*size_y*size_z*size_c; - unsigned int - _size_x = size_x, - _size_y = size_y, - _size_z = size_z, - _size_c = size_c; - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - if (!siz) { // Retrieve file size - const longT fpos = cimg::ftell(nfile); - if (fpos<0) throw CImgArgumentException(_cimg_instance - "load_raw(): Cannot determine size of input file '%s'.", - cimg_instance,filename?filename:"(FILE*)"); - cimg::fseek(nfile,0,SEEK_END); - siz = cimg::ftell(nfile)/sizeof(T); - _size_y = (unsigned int)siz; - _size_x = _size_z = _size_c = 1; - cimg::fseek(nfile,fpos,SEEK_SET); - } - cimg::fseek(nfile,offset,SEEK_SET); - assign(_size_x,_size_y,_size_z,_size_c,0); - if (siz && (!is_multiplexed || size_c==1)) { - cimg::fread(_data,siz,nfile); - if (invert_endianness) cimg::invert_endianness(_data,siz); - } else if (siz) { - CImg<T> buf(1,1,1,_size_c); - cimg_forXYZ(*this,x,y,z) { - cimg::fread(buf._data,_size_c,nfile); - if (invert_endianness) cimg::invert_endianness(buf._data,_size_c); - set_vector_at(buf,x,y,z); - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load image sequence from a YUV file. - /** - \param filename Filename, as a C-string. - \param size_x Width of the frames. - \param size_y Height of the frames. - \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>. - \param first_frame Index of the first frame to read. - \param last_frame Index of the last frame to read. - \param step_frame Step value for frame reading. - \param yuv2rgb Tells if the YUV to RGB transform must be applied. - \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - **/ - CImg<T>& load_yuv(const char *const filename, - const unsigned int size_x, const unsigned int size_y=1, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return get_load_yuv(filename,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); - } - - //! Load image sequence from a YUV file \newinstance. - static CImg<T> get_load_yuv(const char *const filename, - const unsigned int size_x, const unsigned int size_y=1, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); - } - - //! Load image sequence from a YUV file \overloading. - CImg<T>& load_yuv(std::FILE *const file, - const unsigned int size_x, const unsigned int size_y=1, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return get_load_yuv(file,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); - } - - //! Load image sequence from a YUV file \newinstance. - static CImg<T> get_load_yuv(std::FILE *const file, - const unsigned int size_x, const unsigned int size_y=1, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); - } - - //! Load 3D object from a .OFF file. - /** - \param[out] primitives Primitives data of the 3D object. - \param[out] colors Colors data of the 3D object. - \param filename Filename, as a C-string. - **/ - template<typename tf, typename tc> - CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) { - return _load_off(primitives,colors,0,filename); - } - - //! Load 3D object from a .OFF file \newinstance. - template<typename tf, typename tc> - static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) { - return CImg<T>().load_off(primitives,colors,filename); - } - - //! Load 3D object from a .OFF file \overloading. - template<typename tf, typename tc> - CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) { - return _load_off(primitives,colors,file,0); - } - - //! Load 3D object from a .OFF file \newinstance. - template<typename tf, typename tc> - static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) { - return CImg<T>().load_off(primitives,colors,file); - } - - template<typename tf, typename tc> - CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors, - std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "load_off(): Specified filename is (null).", - cimg_instance); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); - unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0; - CImg<charT> line(256); *line = 0; - int err; - - // Skip comments, and read magic string OFF - do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); - if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_off(): OFF header not found in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); - if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_off(): Invalid number of vertices or primitives specified in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - - // Read points data - assign(nb_points,3); - float X = 0, Y = 0, Z = 0; - cimg_forX(*this,l) { - do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); - if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "load_off(): Failed to read vertex %u/%u in file '%s'.", - cimg_instance, - l + 1,nb_points,filename?filename:"(FILE*)"); - } - (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; - } - - // Read primitive data - primitives.assign(); - colors.assign(); - bool stop_flag = false; - while (!stop_flag) { - float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f; - unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0; - *line = 0; - if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true; - else { - ++nb_read; - switch (prim) { - case 1 : { - if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0).move_to(primitives); - CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); - } - } break; - case 2 : { - if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0,i1).move_to(primitives); - CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); - } - } break; - case 3 : { - if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0,i2,i1).move_to(primitives); - CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); - } - } break; - case 4 : { - if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives); - CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); - } - } break; - case 5 : { - if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives); - CImg<tf>::vector(i0,i4,i3).move_to(primitives); - colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); - ++nb_primitives; - } - } break; - case 6 : { - if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives); - CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives); - colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); - ++nb_primitives; - } - } break; - case 7 : { - if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives); - CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives); - CImg<tf>::vector(i3,i2,i1).move_to(primitives); - colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); - ++(++nb_primitives); - } - } break; - case 8 : { - if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line._data))<7) { - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u from file '%s'.", - cimg_instance, - nb_read,nb_primitives,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } else { - err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); - CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives); - CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives); - CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives); - colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); - ++(++nb_primitives); - } - } break; - default : - cimg::warn(_cimg_instance - "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.", - cimg_instance, - nb_read,nb_primitives,prim,filename?filename:"(FILE*)"); - - err = std::fscanf(nfile,"%*[^\n] "); - } - } - } - if (!file) cimg::fclose(nfile); - if (primitives._width!=nb_primitives) - cimg::warn(_cimg_instance - "load_off(): Only %u/%u primitives read from file '%s'.", - cimg_instance, - primitives._width,nb_primitives,filename?filename:"(FILE*)"); - return *this; - } - - //! Load image sequence from a video file, using OpenCV library. - /** - \param filename Filename, as a C-string. - \param first_frame Index of the first frame to read. - \param last_frame Index of the last frame to read. - \param step_frame Step value for frame reading. - \param axis Alignment axis. - \param align Apending alignment. - **/ - CImg<T>& load_video(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - const char axis='z', const float align=0) { - return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this); - } - - //! Load image sequence from a video file, using OpenCV library \newinstance. - static CImg<T> get_load_video(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - const char axis='z', const float align=0) { - return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align); - } - - //! Load image sequence using FFMPEG's external tool 'ffmpeg'. - /** - \param filename Filename, as a C-string. - \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignment. - **/ - CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) { - return get_load_ffmpeg_external(filename,axis,align).move_to(*this); - } - - //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance. - static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) { - return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align); - } - - //! Load gif file, using Imagemagick or GraphicsMagicks's external tools. - /** - \param filename Filename, as a C-string. - \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignment. - **/ - CImg<T>& load_gif_external(const char *const filename, - const char axis='z', const float align=0) { - return get_load_gif_external(filename,axis,align).move_to(*this); - } - - //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance. - static CImg<T> get_load_gif_external(const char *const filename, - const char axis='z', const float align=0) { - return CImgList<T>().load_gif_external(filename).get_append(axis,align); - } - - //! Load image using GraphicsMagick's external tool 'gm'. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_graphicsmagick_external(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_graphicsmagick_external(): Specified filename is (null).", - cimg_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - CImg<charT> command(1024), filename_tmp(256); - std::FILE *file = 0; - const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape(); -#if cimg_OS==1 - if (!cimg::system("which gm")) { - cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-", - cimg::graphicsmagick_path(),s_filename.data()); - file = popen(command,"r"); - if (file) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { load_pnm(file); } catch (...) { - pclose(file); - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load_graphicsmagick_external(): Failed to load file '%s' " - "with external command 'gm'.", - cimg_instance, - filename); - } - pclose(file); - return *this; - } - } -#endif - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s\"", - cimg::graphicsmagick_path(),s_filename.data(), - CImg<charT>::string(filename_tmp)._system_strescape().data()); - cimg::system(command,cimg::graphicsmagick_path()); - if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { - cimg::fclose(cimg::fopen(filename,"r")); - throw CImgIOException(_cimg_instance - "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", - cimg_instance, - filename); - - } else cimg::fclose(file); - load_pnm(filename_tmp); - std::remove(filename_tmp); - return *this; - } - - //! Load image using GraphicsMagick's external tool 'gm' \newinstance. - static CImg<T> get_load_graphicsmagick_external(const char *const filename) { - return CImg<T>().load_graphicsmagick_external(filename); - } - - //! Load gzipped image file, using external tool 'gunzip'. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_gzip_external(const char *const filename) { - if (!filename) - throw CImgIOException(_cimg_instance - "load_gzip_external(): Specified filename is (null).", - cimg_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - CImg<charT> command(1024), filename_tmp(256), body(256); - const char - *const ext = cimg::split_filename(filename,body), - *const ext2 = cimg::split_filename(body,0); - - std::FILE *file = 0; - do { - if (!cimg::strcasecmp(ext,"gz")) { - if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gunzip_path(), - CImg<charT>::string(filename)._system_strescape().data(), - CImg<charT>::string(filename_tmp)._system_strescape().data()); - cimg::system(command); - if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { - cimg::fclose(cimg::fopen(filename,"r")); - throw CImgIOException(_cimg_instance - "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.", - cimg_instance, - filename); - - } else cimg::fclose(file); - load(filename_tmp); - std::remove(filename_tmp); - return *this; - } - - //! Load gzipped image file, using external tool 'gunzip' \newinstance. - static CImg<T> get_load_gzip_external(const char *const filename) { - return CImg<T>().load_gzip_external(filename); - } - - //! Load image using ImageMagick's external tool 'convert'. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_imagemagick_external(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_imagemagick_external(): Specified filename is (null).", - cimg_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - CImg<charT> command(1024), filename_tmp(256); - std::FILE *file = 0; - const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape(); -#if cimg_OS==1 - if (!cimg::system("which convert")) { - cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-", - cimg::imagemagick_path(), - !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", - s_filename.data()); - file = popen(command,"r"); - if (file) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { load_pnm(file); } catch (...) { - pclose(file); - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load_imagemagick_external(): Failed to load file '%s' with " - "external command 'magick/convert'.", - cimg_instance, - filename); - } - pclose(file); - return *this; - } - } -#endif - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s%s \"%s\" \"%s\"", - cimg::imagemagick_path(), - !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", - s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data()); - cimg::system(command,cimg::imagemagick_path()); - if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { - cimg::fclose(cimg::fopen(filename,"r")); - throw CImgIOException(_cimg_instance - "load_imagemagick_external(): Failed to load file '%s' with " - "external command 'magick/convert'.", - cimg_instance, - filename); - - } else cimg::fclose(file); - load_pnm(filename_tmp); - std::remove(filename_tmp); - return *this; - } - - //! Load image using ImageMagick's external tool 'convert' \newinstance. - static CImg<T> get_load_imagemagick_external(const char *const filename) { - return CImg<T>().load_imagemagick_external(filename); - } - - //! Load image from a DICOM file, using XMedcon's external tool 'medcon'. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_medcon_external(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_medcon_external(): Specified filename is (null).", - cimg_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - CImg<charT> command(1024), filename_tmp(256), body(256); - cimg::fclose(cimg::fopen(filename,"r")); - std::FILE *file = 0; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -w -c anlz -o \"%s\" -f \"%s\"", - cimg::medcon_path(), - CImg<charT>::string(filename_tmp)._system_strescape().data(), - CImg<charT>::string(filename)._system_strescape().data()); - cimg::system(command); - cimg::split_filename(filename_tmp,body); - - cimg_snprintf(command,command._width,"%s.hdr",body._data); - file = cimg::std_fopen(command,"rb"); - if (!file) { - cimg_snprintf(command,command._width,"m000-%s.hdr",body._data); - file = cimg::std_fopen(command,"rb"); - if (!file) { - throw CImgIOException(_cimg_instance - "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.", - cimg_instance, - filename); - } - } - cimg::fclose(file); - load_analyze(command); - std::remove(command); - cimg::split_filename(command,body); - cimg_snprintf(command,command._width,"%s.img",body._data); - std::remove(command); - return *this; - } - - //! Load image from a DICOM file, using XMedcon's external tool 'medcon' \newinstance. - static CImg<T> get_load_medcon_external(const char *const filename) { - return CImg<T>().load_medcon_external(filename); - } - - //! Load image from a RAW Color Camera file, using external tool 'dcraw'. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_dcraw_external(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_dcraw_external(): Specified filename is (null).", - cimg_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - CImg<charT> command(1024), filename_tmp(256); - std::FILE *file = 0; - const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape(); -#if cimg_OS==1 - cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"", - cimg::dcraw_path(),s_filename.data()); - file = popen(command,"r"); - if (file) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { load_pnm(file); } catch (...) { - pclose(file); - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", - cimg_instance, - filename); - } - pclose(file); - return *this; - } -#endif - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\" > \"%s\"", - cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data()); - cimg::system(command,cimg::dcraw_path()); - if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { - cimg::fclose(cimg::fopen(filename,"r")); - throw CImgIOException(_cimg_instance - "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", - cimg_instance, - filename); - - } else cimg::fclose(file); - load_pnm(filename_tmp); - std::remove(filename_tmp); - return *this; - } - - //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance. - static CImg<T> get_load_dcraw_external(const char *const filename) { - return CImg<T>().load_dcraw_external(filename); - } - - //! Load image from a camera stream, using OpenCV. - /** - \param camera_index Index of the camera to capture images from. - \param skip_frames Number of frames to skip before the capture. - \param release_camera Tells if the camera ressource must be released at the end of the method. - \param capture_width Width of the desired image. - \param capture_height Height of the desired image. - **/ - CImg<T>& load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0, - const bool release_camera=true, const unsigned int capture_width=0, - const unsigned int capture_height=0) { -#ifdef cimg_use_opencv - if (camera_index>99) - throw CImgArgumentException(_cimg_instance - "load_camera(): Invalid request for camera #%u " - "(no more than 100 cameras can be managed simultaneously).", - cimg_instance, - camera_index); - static CvCapture *capture[100] = { 0 }; - static unsigned int capture_w[100], capture_h[100]; - if (release_camera) { - cimg::mutex(9); - if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index])); - capture[camera_index] = 0; - capture_w[camera_index] = capture_h[camera_index] = 0; - cimg::mutex(9,0); - return *this; - } - if (!capture[camera_index]) { - cimg::mutex(9); - capture[camera_index] = cvCreateCameraCapture(camera_index); - capture_w[camera_index] = 0; - capture_h[camera_index] = 0; - cimg::mutex(9,0); - if (!capture[camera_index]) { - throw CImgIOException(_cimg_instance - "load_camera(): Failed to initialize camera #%u.", - cimg_instance, - camera_index); - } - } - cimg::mutex(9); - if (capture_width!=capture_w[camera_index]) { - cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width); - capture_w[camera_index] = capture_width; - } - if (capture_height!=capture_h[camera_index]) { - cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height); - capture_h[camera_index] = capture_height; - } - const IplImage *img = 0; - for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[camera_index]); - img = cvQueryFrame(capture[camera_index]); - if (img) { - const int step = (int)(img->widthStep - 3*img->width); - assign(img->width,img->height,1,3); - const unsigned char* ptrs = (unsigned char*)img->imageData; - T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - if (step>0) cimg_forY(*this,y) { - cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); } - ptrs+=step; - } else for (ulongT siz = (ulongT)img->width*img->height; siz; --siz) { - *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); - } - } - cimg::mutex(9,0); - return *this; -#else - cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height); - throw CImgIOException(_cimg_instance - "load_camera(): This function requires the OpenCV library to run " - "(macro 'cimg_use_opencv' must be defined).", - cimg_instance); -#endif - } - - //! Load image from a camera stream, using OpenCV \newinstance. - static CImg<T> get_load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0, - const bool release_camera=true, - const unsigned int capture_width=0, const unsigned int capture_height=0) { - return CImg<T>().load_camera(camera_index,skip_frames,release_camera,capture_width,capture_height); - } - - //! Load image using various non-native ways. - /** - \param filename Filename, as a C-string. - **/ - CImg<T>& load_other(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimg_instance - "load_other(): Specified filename is (null).", - cimg_instance); - - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { load_magick(filename); } - catch (CImgException&) { - try { load_imagemagick_external(filename); } - catch (CImgException&) { - try { load_graphicsmagick_external(filename); } - catch (CImgException&) { - try { load_cimg(filename); } - catch (CImgException&) { - try { - cimg::fclose(cimg::fopen(filename,"rb")); - } catch (CImgException&) { - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load_other(): Failed to open file '%s'.", - cimg_instance, - filename); - } - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load_other(): Failed to recognize format of file '%s'.", - cimg_instance, - filename); - } - } - } - } - cimg::exception_mode(omode); - return *this; - } - - //! Load image using various non-native ways \newinstance. - static CImg<T> get_load_other(const char *const filename) { - return CImg<T>().load_other(filename); - } - - //@} - //--------------------------- - // - //! \name Data Output - //@{ - //--------------------------- - - //! Display information about the image data. - /** - \param title Name for the considered image. - \param display_stats Tells to compute and display image statistics. - **/ - const CImg<T>& print(const char *const title=0, const bool display_stats=true) const { - - int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; - CImg<doubleT> st; - if (!is_empty() && display_stats) { - st = get_stats(); - xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7]; - xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11]; - } - - const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1, - mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1; - - CImg<charT> _title(64); - if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type()); - - std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p", - cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, - cimg::t_bold,cimg::t_normal,(void*)this, - cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum, - (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))), - mdisp==0?"b":(mdisp==1?"Kio":"Mio"), - cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); - if (_data) - std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared"); - else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared"); - - if (!is_empty()) cimg_foroff(*this,off) { - std::fprintf(cimg::output(),"%g",(double)_data[off]); - if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" "); - if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); } - } - if (!is_empty() && display_stats) - std::fprintf(cimg::output(), - " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), " - "%scoords_max%s = (%u,%u,%u,%u).\n", - cimg::t_bold,cimg::t_normal,st[0], - cimg::t_bold,cimg::t_normal,st[1], - cimg::t_bold,cimg::t_normal,st[2], - cimg::t_bold,cimg::t_normal,std::sqrt(st[3]), - cimg::t_bold,cimg::t_normal,xm,ym,zm,vm, - cimg::t_bold,cimg::t_normal,xM,yM,zM,vM); - else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" "); - std::fflush(cimg::output()); - return *this; - } - - //! Display image into a CImgDisplay window. - /** - \param disp Display window. - **/ - const CImg<T>& display(CImgDisplay& disp) const { - disp.display(*this); - return *this; - } - - //! Display image into a CImgDisplay window, in an interactive way. - /** - \param disp Display window. - \param display_info Tells if image information are displayed on the standard output. - \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. - \param exit_on_anykey Exit function when any key is pressed. - **/ - const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0, - const bool exit_on_anykey=false) const { - return _display(disp,0,display_info,XYZ,exit_on_anykey,false); - } - - //! Display image into an interactive window. - /** - \param title Window title - \param display_info Tells if image information are displayed on the standard output. - \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. - \param exit_on_anykey Exit function when any key is pressed. - **/ - const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0, - const bool exit_on_anykey=false) const { - CImgDisplay disp; - return _display(disp,title,display_info,XYZ,exit_on_anykey,false); - } - - const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info, - unsigned int *const XYZ, const bool exit_on_anykey, - const bool exit_on_simpleclick) const { - unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0; - int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1, - old_mouse_x = -1, old_mouse_y = -1; - - if (!disp) { - disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); - if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); - else disp.set_title("%s",title); - } else if (title) disp.set_title("%s",title); - disp.show().flush(); - - const CImg<char> dtitle = CImg<char>::string(disp.title()); - if (display_info) print(dtitle); - - CImg<T> zoom; - for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) { - if (reset_view) { - if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; } - else { - _XYZ[0] = (unsigned int)(x0 + x1)/2; - _XYZ[1] = (unsigned int)(y0 + y1)/2; - _XYZ[2] = (unsigned int)(z0 + z1)/2; - } - x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1; - disp.resize(cimg_fitscreen(_width,_height,_depth),false); - oldw = disp._width; oldh = disp._height; - resize_disp = true; - reset_view = false; - } - if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) { - if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign(); - } else zoom = get_crop(x0,y0,z0,x1,y1,z1); - - const CImg<T>& visu = zoom?zoom:*this; - const unsigned int - dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0, - tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U); - if (!is_empty() && !disp.is_fullscreen() && resize_disp) { - const float - ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh, - dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height()); - const unsigned int - imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM); - disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false); - resize_disp = false; - } - oldw = tw; oldh = th; - - bool - go_up = false, go_down = false, go_left = false, go_right = false, - go_inc = false, go_dec = false, go_in = false, go_out = false, - go_in_center = false; - - disp.set_title("%s",dtitle._data); - if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0); - if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0); - if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0); - - disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; - CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true); - old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y; - is_first_select = false; - - if (disp.wheel()) { - if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) && - (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) { - go_left = !(go_right = disp.wheel()>0); - } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { - go_down = !(go_up = disp.wheel()>0); - } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - go_out = !(go_in = disp.wheel()>0); go_in_center = false; - } - disp.set_wheel(); - } - - const int - sx0 = selection(0), sy0 = selection(1), sz0 = selection(2), - sx1 = selection(3), sy1 = selection(4), sz1 = selection(5); - if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) { - x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; - x0+=sx0; y0+=sy0; z0+=sz0; - if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) { - if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true; - } - resize_disp = true; - } else switch (key = disp.key()) { -#if cimg_OS!=2 - case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : -#endif - case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break; - case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { - // Special mode: play stack of frames - const unsigned int - w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)), - h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0)); - float frame_timing = 5; - bool is_stopped = false; - disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0; - for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) { - if (disp.is_resized()) disp.resize(false); - if (!timer) { - visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2])); - (++_XYZ[2])%=visu._depth; - } - if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U; - if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); } - switch (key = disp.key()) { -#if cimg_OS!=2 - case cimg::keyCTRLRIGHT : -#endif - case cimg::keyCTRLLEFT : key = 0; break; - case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break; - case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break; - case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break; - case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break; - case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; - (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break; - case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), - CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false); - disp.set_key(key,false); key = 0; - } break; - case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0; - } break; - case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0; - } break; - case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.resize(disp.screen_width(),disp.screen_height(),false). - toggle_fullscreen().set_key(key,false); key = 0; - } break; - } - frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing); - disp.wait(20); - } - const unsigned int - w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width, - h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height; - disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel(); - key = 0; - } break; - case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break; - case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break; - case cimg::keyPADSUB : go_out = true; key = 0; break; - case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break; - case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break; - case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break; - case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break; - case cimg::keyPAD7 : go_up = go_left = true; key = 0; break; - case cimg::keyPAD9 : go_up = go_right = true; key = 0; break; - case cimg::keyPAD1 : go_down = go_left = true; key = 0; break; - case cimg::keyPAD3 : go_down = go_right = true; key = 0; break; - case cimg::keyPAGEUP : go_inc = true; key = 0; break; - case cimg::keyPAGEDOWN : go_dec = true; key = 0; break; - } - if (go_in) { - const int - mx = go_in_center?disp.width()/2:disp.mouse_x(), - my = go_in_center?disp.height()/2:disp.mouse_y(), - mX = mx*(width() + (depth()>1?depth():0))/disp.width(), - mY = my*(height() + (depth()>1?depth():0))/disp.height(); - int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2]; - if (mX<width() && mY<height()) { - X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height(); - } - if (mX<width() && mY>=height()) { - X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); - } - if (mX>=width() && mY<height()) { - Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth(); - } - if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; } - if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; } - if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; } - } - if (go_out) { - const int - delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8, - ndelta_x = delta_x?delta_x:(_width>1), - ndelta_y = delta_y?delta_y:(_height>1), - ndelta_z = delta_z?delta_z:(_depth>1); - x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z; - x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z; - if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; } - if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; } - if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; } - if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; } - if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; } - if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; } - const float - ratio = (float)(x1-x0)/(y1-y0), - ratiow = (float)disp._width/disp._height, - sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow)); - if (sub>0.01) resize_disp = true; - } - if (go_left) { - const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); - if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } - else { x1-=x0; x0 = 0; } - } - if (go_right) { - const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); - if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; } - else { x0+=(width() - 1 - x1); x1 = width() - 1; } - } - if (go_up) { - const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1); - if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; } - else { y1-=y0; y0 = 0; } - } - if (go_down) { - const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1); - if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; } - else { y0+=(height() - 1 - y1); y1 = height() - 1; } - } - if (go_inc) { - const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1); - if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; } - else { z1-=z0; z0 = 0; } - } - if (go_dec) { - const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1); - if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; } - else { z0+=(depth() - 1 - z1); z1 = depth() - 1; } - } - disp.wait(100); - if (!exit_on_anykey && key && key!=cimg::keyESC && - (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - key = 0; - } - } - disp.set_key(key); - if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; } - return *this; - } - - //! Display object 3D in an interactive window. - /** - \param disp Display window. - \param vertices Vertices data of the 3D object. - \param primitives Primitives data of the 3D object. - \param colors Colors data of the 3D object. - \param opacities Opacities data of the 3D object. - \param centering Tells if the 3D object must be centered for the display. - \param render_static Rendering mode. - \param render_motion Rendering mode, when the 3D object is moved. - \param is_double_sided Tells if the object primitives are double-sided. - \param focale Focale - \param light_x X-coordinate of the light source. - \param light_y Y-coordinate of the light source. - \param light_z Z-coordinate of the light source. - \param specular_lightness Amount of specular light. - \param specular_shininess Shininess of the object material. - \param display_axes Tells if the 3D axes are displayed. - \param pose_matrix Pointer to 12 values, defining a 3D pose (as a 4x3 matrix). - \param exit_on_anykey Exit function when any key is pressed. - **/ - template<typename tp, typename tf, typename tc, typename to> - const CImg<T>& display_object3d(CImgDisplay& disp, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const to& opacities, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static, - render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - //! Display object 3D in an interactive window \simplification. - template<typename tp, typename tf, typename tc, typename to> - const CImg<T>& display_object3d(const char *const title, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const to& opacities, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - CImgDisplay disp; - return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static, - render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - //! Display object 3D in an interactive window \simplification. - template<typename tp, typename tf, typename tc> - const CImg<T>& display_object3d(CImgDisplay &disp, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - //! Display object 3D in an interactive window \simplification. - template<typename tp, typename tf, typename tc> - const CImg<T>& display_object3d(const char *const title, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - //! Display object 3D in an interactive window \simplification. - template<typename tp, typename tf> - const CImg<T>& display_object3d(CImgDisplay &disp, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - return display_object3d(disp,vertices,primitives,CImgList<T>(),centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - - //! Display object 3D in an interactive window \simplification. - template<typename tp, typename tf> - const CImg<T>& display_object3d(const char *const title, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - return display_object3d(title,vertices,primitives,CImgList<T>(),centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - //! Display object 3D in an interactive window \simplification. - template<typename tp> - const CImg<T>& display_object3d(CImgDisplay &disp, - const CImg<tp>& vertices, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - return display_object3d(disp,vertices,CImgList<uintT>(),centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - //! Display object 3D in an interactive window \simplification. - template<typename tp> - const CImg<T>& display_object3d(const char *const title, - const CImg<tp>& vertices, - const bool centering=true, - const int render_static=4, const int render_motion=1, - const bool is_double_sided=true, const float focale=700, - const float light_x=0, const float light_y=0, const float light_z=-5e8f, - const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0, - const bool exit_on_anykey=false) const { - return display_object3d(title,vertices,CImgList<uintT>(),centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - - template<typename tp, typename tf, typename tc, typename to> - const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title, - const CImg<tp>& vertices, - const CImgList<tf>& primitives, - const CImgList<tc>& colors, - const to& opacities, - const bool centering, - const int render_static, const int render_motion, - const bool is_double_sided, const float focale, - const float light_x, const float light_y, const float light_z, - const float specular_lightness, const float specular_shininess, - const bool display_axes, float *const pose_matrix, - const bool exit_on_anykey) const { - typedef typename cimg::superset<tp,float>::type tpfloat; - - // Check input arguments - if (is_empty()) { - if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0). - _display_object3d(disp,title,vertices,primitives,colors,opacities,centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - else return CImg<T>(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2, - CImgDisplay::screen_height()/2,1), - 1,(colors && colors[0].size()==1)?1:3,3). - _display_object3d(disp,title,vertices,primitives,colors,opacities,centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } else { if (disp) disp.resize(*this,false); } - CImg<charT> error_message(1024); - if (!vertices.is_object3d(primitives,colors,opacities,true,error_message)) - throw CImgArgumentException(_cimg_instance - "display_object3d(): Invalid specified 3D object (%u,%u) (%s).", - cimg_instance,vertices._width,primitives._width,error_message.data()); - if (vertices._width && !primitives) { - CImgList<tf> nprimitives(vertices._width,1,1,1,1); - cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l; - return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, - render_static,render_motion,is_double_sided,focale, - light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix,exit_on_anykey); - } - if (!disp) { - disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); - if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)", - pixel_type(),vertices._width,primitives._width); - } else if (title) disp.set_title("%s",title); - - // Init 3D objects and compute object statistics - CImg<floatT> - pose, - rotated_vertices(vertices._width,3), - bbox_vertices, rotated_bbox_vertices, - axes_vertices, rotated_axes_vertices, - bbox_opacities, axes_opacities; - CImgList<uintT> bbox_primitives, axes_primitives; - CImgList<tf> reverse_primitives; - CImgList<T> bbox_colors, bbox_colors2, axes_colors; - unsigned int ns_width = 0, ns_height = 0; - int _is_double_sided = (int)is_double_sided; - bool ndisplay_axes = display_axes; - const CImg<T> - background_color(1,1,1,_spectrum,0), - foreground_color(1,1,1,_spectrum,255); - float - Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1, - xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0, - ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0, - zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0; - const float delta = cimg::max(xM - xm,yM - ym,zM - zm); - - rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1, - xm,xM,xM,xm,xm,xM,xM,xm, - ym,ym,yM,yM,ym,ym,yM,yM, - zm,zm,zm,zm,zM,zM,zM,zM); - bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6); - bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]); - bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]); - bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f); - - rotated_axes_vertices = axes_vertices.assign(7,3,1,1, - 0,20,0,0,22,-6,-6, - 0,0,20,0,-6,22,-6, - 0,0,0,20,0,0,22); - axes_opacities.assign(3,1,1,1,1); - axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]); - axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); - - // Begin user interaction loop - CImg<T> visu0(*this,false), visu; - CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0); - bool init_pose = true, clicked = false, redraw = true; - unsigned int key = 0; - int - x0 = 0, y0 = 0, x1 = 0, y1 = 0, - nrender_static = render_static, - nrender_motion = render_motion; - disp.show().flush(); - - while (!disp.is_closed() && !key) { - - // Init object pose - if (init_pose) { - const float - ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1, - dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2; - if (centering) - CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose); - else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose); - if (pose_matrix) { - CImg<floatT> pose0(pose_matrix,4,3,1,1,false); - pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0); - pose0(3,3) = pose(3,3) = 1; - (pose0*pose).get_crop(0,0,3,2).move_to(pose); - Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15]; - } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; } - init_pose = false; - redraw = true; - } - - // Rotate and draw 3D object - if (redraw) { - const float - r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), - r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), - r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); - if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) - cimg_forX(vertices,l) { - const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2); - rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30; - rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31; - rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32; - } - else cimg_forX(bbox_vertices,l) { - const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2); - rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30; - rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31; - rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32; - } - - // Draw objects - const bool render_with_zbuffer = !clicked && nrender_static>0; - visu = visu0; - if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0)) - visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, - rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale). - draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, - rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale); - else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(), - Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, - rotated_vertices,reverse_primitives?reverse_primitives:primitives, - colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale, - width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff, - specular_lightness,specular_shininess,1,sprite_scale); - // Draw axes - if (ndisplay_axes) { - const float - n = 1e-8f + cimg::hypot(r00,r01,r02), - _r00 = r00/n, _r10 = r10/n, _r20 = r20/n, - _r01 = r01/n, _r11 = r11/n, _r21 = r21/n, - _r02 = r01/n, _r12 = r12/n, _r22 = r22/n, - Xaxes = 25, Yaxes = visu._height - 38.f; - cimg_forX(axes_vertices,l) { - const float - x = axes_vertices(l,0), - y = axes_vertices(l,1), - z = axes_vertices(l,2); - rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z; - rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z; - rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z; - } - axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f; - axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f; - axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f; - visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives, - axes_colors,axes_opacities,1,false,focale). - draw_text((int)(Xaxes + rotated_axes_vertices(4,0)), - (int)(Yaxes + rotated_axes_vertices(4,1)), - "X",axes_colors[0]._data,0,axes_opacities(0,0),13). - draw_text((int)(Xaxes + rotated_axes_vertices(5,0)), - (int)(Yaxes + rotated_axes_vertices(5,1)), - "Y",axes_colors[1]._data,0,axes_opacities(1,0),13). - draw_text((int)(Xaxes + rotated_axes_vertices(6,0)), - (int)(Yaxes + rotated_axes_vertices(6,1)), - "Z",axes_colors[2]._data,0,axes_opacities(2,0),13); - } - visu.display(disp); - if (!clicked || nrender_motion==nrender_static) redraw = false; - } - - // Handle user interaction - disp.wait(); - if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) { - redraw = true; - if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; } - else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); } - if (disp.button()&1) { - const float - R = 0.45f*std::min(disp.width(),disp.height()), - R2 = R*R, - u0 = (float)(x0 - disp.width()/2), - v0 = (float)(y0 - disp.height()/2), - u1 = (float)(x1 - disp.width()/2), - v1 = (float)(y1 - disp.height()/2), - n0 = cimg::hypot(u0,v0), - n1 = cimg::hypot(u1,v1), - nu0 = n0>R?(u0*R/n0):u0, - nv0 = n0>R?(v0*R/n0):v0, - nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), - nu1 = n1>R?(u1*R/n1):u1, - nv1 = n1>R?(v1*R/n1):v1, - nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), - u = nv0*nw1 - nw0*nv1, - v = nw0*nu1 - nu0*nw1, - w = nv0*nu1 - nu0*nv1, - n = cimg::hypot(u,v,w), - alpha = (float)std::asin(n/R2)*180/cimg::PI; - (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose); - x0 = x1; y0 = y1; - } - if (disp.button()&2) { - if (focale>0) Zoff-=(y0 - y1)*focale/400; - else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; } - x0 = x1; y0 = y1; - } - if (disp.wheel()) { - if (focale>0) Zoff-=disp.wheel()*focale/20; - else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; } - disp.set_wheel(); - } - if (disp.button()&4) { Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1; } - if ((disp.button()&1) && (disp.button()&2)) { - init_pose = true; disp.set_button(); x0 = x1; y0 = y1; - pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0); - } - } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } - - CImg<charT> filename(32); - switch (key = disp.key()) { -#if cimg_OS!=2 - case cimg::keyCTRLRIGHT : -#endif - case 0 : case cimg::keyCTRLLEFT : key = 0; break; - case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), - CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). - _is_resized = true; - disp.set_key(key,false); key = 0; - } break; - case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; - disp.set_key(key,false); key = 0; - } break; - case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; - disp.set_key(key,false); key = 0; - } break; - case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - if (!ns_width || !ns_height || - ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) { - ns_width = disp.screen_width()*3U/4; - ns_height = disp.screen_height()*3U/4; - } - if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false); - else { - ns_width = disp._width; ns_height = disp._height; - disp.resize(disp.screen_width(),disp.screen_height(),false); - } - disp.toggle_fullscreen()._is_resized = true; - disp.set_key(key,false); key = 0; - } break; - case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - // Switch single/double-sided primitives. - if (--_is_double_sided==-2) _is_double_sided = 1; - if (_is_double_sided>=0) reverse_primitives.assign(); - else primitives.get_reverse_object3d().move_to(reverse_primitives); - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer - if (zbuffer) zbuffer.assign(); - else zbuffer.assign(visu0.width(),visu0.height(),1,1,0); - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes - ndisplay_axes = !ndisplay_axes; - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points - nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0; - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines - nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1; - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat - nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2; - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded - nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3; - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - // Set rendering mode to gouraud-shaded. - nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4; - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded - nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5; - disp.set_key(key,false); key = 0; redraw = true; - } break; - case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot - static unsigned int snap_number = 0; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+visu).__draw_text(" Saving snapshot... ",false).display(disp); - visu.save(filename); - (+visu).__draw_text(" Snapshot '%s' saved. ",false,filename._data).display(disp); - disp.set_key(key,false); key = 0; - } break; - case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file - static unsigned int snap_number = 0; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++); - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+visu).__draw_text(" Saving object... ",false).display(disp); - vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename); - (+visu).__draw_text(" Object '%s' saved. ",false,filename._data).display(disp); - disp.set_key(key,false); key = 0; - } break; - case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file - static unsigned int snap_number = 0; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); -#endif - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+visu).__draw_text(" Saving object... ",false).display(disp); - vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities). - save(filename); - (+visu).__draw_text(" Object '%s' saved. ",false,filename._data).display(disp); - disp.set_key(key,false); key = 0; - } break; -#ifdef cimg_use_board - case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file - static unsigned int snap_number = 0; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++); - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+visu).__draw_text(" Saving EPS snapshot... ",false).display(disp); - LibBoard::Board board; - (+visu)._draw_object3d(&board,zbuffer.fill(0), - Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, - rotated_vertices,reverse_primitives?reverse_primitives:primitives, - colors,opacities,clicked?nrender_motion:nrender_static, - _is_double_sided==1,focale, - visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, - specular_lightness,specular_shininess,1, - sprite_scale); - board.saveEPS(filename); - (+visu).__draw_text(" Object '%s' saved. ",false,filename._data).display(disp); - disp.set_key(key,false); key = 0; - } break; - case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file - static unsigned int snap_number = 0; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++); - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+visu).__draw_text(" Saving SVG snapshot... ",false,13).display(disp); - LibBoard::Board board; - (+visu)._draw_object3d(&board,zbuffer.fill(0), - Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, - rotated_vertices,reverse_primitives?reverse_primitives:primitives, - colors,opacities,clicked?nrender_motion:nrender_static, - _is_double_sided==1,focale, - visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, - specular_lightness,specular_shininess,1, - sprite_scale); - board.saveSVG(filename); - (+visu).__draw_text(" Object '%s' saved. ",false,filename._data).display(disp); - disp.set_key(key,false); key = 0; - } break; -#endif - } - if (disp.is_resized()) { - disp.resize(false); visu0 = get_resize(disp,1); - if (zbuffer) zbuffer.assign(disp.width(),disp.height()); - redraw = true; - } - if (!exit_on_anykey && key && key!=cimg::keyESC && - (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - key = 0; - } - } - if (pose_matrix) { - std::memcpy(pose_matrix,pose._data,12*sizeof(float)); - pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale; - } - disp.set_button().set_key(key); - return *this; - } - - //! Display 1D graph in an interactive window. - /** - \param disp Display window. - \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>. - \param vertex_type Vertex type. - \param labelx Title for the horizontal axis, as a C-string. - \param xmin Minimum value along the X-axis. - \param xmax Maximum value along the X-axis. - \param labely Title for the vertical axis, as a C-string. - \param ymin Minimum value along the X-axis. - \param ymax Maximum value along the X-axis. - \param exit_on_anykey Exit function when any key is pressed. - **/ - const CImg<T>& display_graph(CImgDisplay &disp, - const unsigned int plot_type=1, const unsigned int vertex_type=1, - const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0, - const bool exit_on_anykey=false) const { - return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); - } - - //! Display 1D graph in an interactive window \overloading. - const CImg<T>& display_graph(const char *const title=0, - const unsigned int plot_type=1, const unsigned int vertex_type=1, - const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0, - const bool exit_on_anykey=false) const { - CImgDisplay disp; - return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); - } - - const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0, - const unsigned int plot_type=1, const unsigned int vertex_type=1, - const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0, - const bool exit_on_anykey=false) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "display_graph(): Empty instance.", - cimg_instance); - if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). - set_title(title?"%s":"CImg<%s>",title?title:pixel_type()); - const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1); - const unsigned int old_normalization = disp.normalization(); - disp.show().flush()._normalization = 0; - - double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax; - if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; } - int x0 = 0, x1 = width()*height()*depth() - 1, key = 0; - - for (bool reset_view = true; !key && !disp.is_closed(); ) { - if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; } - CImg<T> zoom(x1 - x0 + 1,1,1,spectrum()); - cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true); - if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; } - if (y0==y1) { --y0; ++y1; } - - const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type, - labelx, - nxmin + x0*(nxmax - nxmin)/siz1, - nxmin + x1*(nxmax - nxmin)/siz1, - labely,y0,y1,true); - const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); - if (selection[0]>=0) { - if (selection[2]<0) reset_view = true; - else { - x1 = x0 + selection[2]; x0+=selection[0]; - if (selection[1]>=0 && selection[3]>=0) { - y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32); - y1-=selection[1]*(y1 - y0)/(disp.height() - 32); - } - } - } else { - bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false; - switch (key = (int)disp.key()) { - case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break; - case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break; - case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break; - case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); - break; - case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); - break; - case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break; - case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break; - case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break; - case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break; - case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break; - case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break; - } - if (disp.wheel()) { - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0); - else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0); - else go_out = !(go_in = disp.wheel()>0); - key = 0; - } - - if (go_in) { - const int - xsiz = x1 - x0, - mx = (mouse_x - 16)*xsiz/(disp.width() - 32), - cx = x0 + cimg::cut(mx,0,xsiz); - if (x1 - x0>4) { - x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8; - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - const double - ysiz = y1 - y0, - my = (mouse_y - 16)*ysiz/(disp.height() - 32), - cy = y1 - cimg::cut(my,0.,ysiz); - y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8; - } else y0 = y1 = 0; - } - } - if (go_out) { - if (x0>0 || x1<(int)siz1) { - const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1); - const double ndelta_y = (y1 - y0)/8; - x0-=ndelta_x; x1+=ndelta_x; - y0-=ndelta_y; y1+=ndelta_y; - if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; } - if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; } - } - } - if (go_left) { - const int delta = (x1 - x0)/5, ndelta = delta?delta:1; - if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } - else { x1-=x0; x0 = 0; } - go_left = false; - } - if (go_right) { - const int delta = (x1 - x0)/5, ndelta = delta?delta:1; - if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; } - else { x0+=(siz1 - x1); x1 = (int)siz1; } - go_right = false; - } - if (go_up) { - const double delta = (y1 - y0)/10, ndelta = delta?delta:1; - y0+=ndelta; y1+=ndelta; - go_up = false; - } - if (go_down) { - const double delta = (y1 - y0)/10, ndelta = delta?delta:1; - y0-=ndelta; y1-=ndelta; - go_down = false; - } - } - if (!exit_on_anykey && key && key!=(int)cimg::keyESC && - (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - disp.set_key(key,false); - key = 0; - } - } - disp._normalization = old_normalization; - return *this; - } - - //! Save image as a file. - /** - \param filename Filename, as a C-string. - \param number When positive, represents an index added to the filename. Otherwise, no number is added. - \param digits Number of digits used for adding the number to the filename. - \note - - The used file format is defined by the file extension in the filename \p filename. - - Parameter \p number can be used to add a 6-digit number to the filename before saving. - - **/ - const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save(): Specified filename is (null).", - cimg_instance); - // Do not test for empty instances, since .cimg format is able to manage empty instances. - const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); - const char *const ext = cimg::split_filename(filename); - CImg<charT> nfilename(1024); - const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename): - filename; - -#ifdef cimg_save_plugin - cimg_save_plugin(fn); -#endif -#ifdef cimg_save_plugin1 - cimg_save_plugin1(fn); -#endif -#ifdef cimg_save_plugin2 - cimg_save_plugin2(fn); -#endif -#ifdef cimg_save_plugin3 - cimg_save_plugin3(fn); -#endif -#ifdef cimg_save_plugin4 - cimg_save_plugin4(fn); -#endif -#ifdef cimg_save_plugin5 - cimg_save_plugin5(fn); -#endif -#ifdef cimg_save_plugin6 - cimg_save_plugin6(fn); -#endif -#ifdef cimg_save_plugin7 - cimg_save_plugin7(fn); -#endif -#ifdef cimg_save_plugin8 - cimg_save_plugin8(fn); -#endif - // Ascii formats - if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn); - else if (!cimg::strcasecmp(ext,"dlm") || - !cimg::strcasecmp(ext,"txt")) return save_dlm(fn); - else if (!cimg::strcasecmp(ext,"cpp") || - !cimg::strcasecmp(ext,"hpp") || - !cimg::strcasecmp(ext,"h") || - !cimg::strcasecmp(ext,"c")) return save_cpp(fn); - - // 2D binary formats - else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn); - else if (!cimg::strcasecmp(ext,"jpg") || - !cimg::strcasecmp(ext,"jpeg") || - !cimg::strcasecmp(ext,"jpe") || - !cimg::strcasecmp(ext,"jfif") || - !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn); - else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn); - else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn); - else if (!cimg::strcasecmp(ext,"png")) return save_png(fn); - else if (!cimg::strcasecmp(ext,"pgm") || - !cimg::strcasecmp(ext,"ppm") || - !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn); - else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn); - else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn); - else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn); - else if (!cimg::strcasecmp(ext,"tif") || - !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); - - // 3D binary formats - else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); - else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); - else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn); - else if (!cimg::strcasecmp(ext,"hdr") || - !cimg::strcasecmp(ext,"nii")) return save_analyze(fn); - else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn); - else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn); - else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn); - else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn); - - // Archive files - else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); - - // Image sequences - else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); - else if (!cimg::strcasecmp(ext,"avi") || - !cimg::strcasecmp(ext,"mov") || - !cimg::strcasecmp(ext,"asf") || - !cimg::strcasecmp(ext,"divx") || - !cimg::strcasecmp(ext,"flv") || - !cimg::strcasecmp(ext,"mpg") || - !cimg::strcasecmp(ext,"m1v") || - !cimg::strcasecmp(ext,"m2v") || - !cimg::strcasecmp(ext,"m4v") || - !cimg::strcasecmp(ext,"mjp") || - !cimg::strcasecmp(ext,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !cimg::strcasecmp(ext,"qt") || - !cimg::strcasecmp(ext,"rm") || - !cimg::strcasecmp(ext,"vob") || - !cimg::strcasecmp(ext,"wmv") || - !cimg::strcasecmp(ext,"xvid") || - !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); - return save_other(fn); - } - - //! Save image as an Ascii file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_ascii(const char *const filename) const { - return _save_ascii(0,filename); - } - - //! Save image as an Ascii file \overloading. - const CImg<T>& save_ascii(std::FILE *const file) const { - return _save_ascii(file,0); - } - - const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_ascii(): Specified filename is (null).", - cimg_instance); - std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); - std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum); - const T* ptrs = _data; - cimg_forYZC(*this,y,z,c) { - cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++)); - std::fputc('\n',nfile); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a .cpp source file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_cpp(const char *const filename) const { - return _save_cpp(0,filename); - } - - //! Save image as a .cpp source file \overloading. - const CImg<T>& save_cpp(std::FILE *const file) const { - return _save_cpp(file,0); - } - - const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_cpp(): Specified filename is (null).", - cimg_instance); - std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); - CImg<charT> varname(1024); *varname = 0; - if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data); - if (!*varname) cimg_snprintf(varname,varname._width,"unnamed"); - std::fprintf(nfile, - "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" - "%s data_%s[] = { %s\n ", - varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data, - is_empty()?"};":""); - if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) { - std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off])); - if (off==siz) std::fprintf(nfile," };\n"); - else if (!((off + 1)%16)) std::fprintf(nfile,",\n "); - else std::fprintf(nfile,", "); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a DLM file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_dlm(const char *const filename) const { - return _save_dlm(0,filename); - } - - //! Save image as a DLM file \overloading. - const CImg<T>& save_dlm(std::FILE *const file) const { - return _save_dlm(file,0); - } - - const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_dlm(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - if (_spectrum>1) - cimg::warn(_cimg_instance - "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); - const T* ptrs = _data; - cimg_forYZC(*this,y,z,c) { - cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":","); - std::fputc('\n',nfile); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a BMP file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_bmp(const char *const filename) const { - return _save_bmp(0,filename); - } - - //! Save image as a BMP file \overloading. - const CImg<T>& save_bmp(std::FILE *const file) const { - return _save_bmp(file,0); - } - - const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_bmp(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - if (_spectrum>3) - cimg::warn(_cimg_instance - "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - CImg<ucharT> header(54,1,1,1,0); - unsigned char align_buf[4] = { 0 }; - const unsigned int - align = (4 - (3*_width)%4)%4, - buf_size = (3*_width + align)*height(), - file_size = 54 + buf_size; - header[0] = 'B'; header[1] = 'M'; - header[0x02] = file_size&0xFF; - header[0x03] = (file_size>>8)&0xFF; - header[0x04] = (file_size>>16)&0xFF; - header[0x05] = (file_size>>24)&0xFF; - header[0x0A] = 0x36; - header[0x0E] = 0x28; - header[0x12] = _width&0xFF; - header[0x13] = (_width>>8)&0xFF; - header[0x14] = (_width>>16)&0xFF; - header[0x15] = (_width>>24)&0xFF; - header[0x16] = _height&0xFF; - header[0x17] = (_height>>8)&0xFF; - header[0x18] = (_height>>16)&0xFF; - header[0x19] = (_height>>24)&0xFF; - header[0x1A] = 1; - header[0x1B] = 0; - header[0x1C] = 24; - header[0x1D] = 0; - header[0x22] = buf_size&0xFF; - header[0x23] = (buf_size>>8)&0xFF; - header[0x24] = (buf_size>>16)&0xFF; - header[0x25] = (buf_size>>24)&0xFF; - header[0x27] = 0x1; - header[0x2B] = 0x1; - cimg::fwrite(header._data,54,nfile); - - const T - *ptr_r = data(0,_height - 1,0,0), - *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0, - *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0; - - switch (_spectrum) { - case 1 : { - cimg_forY(*this,y) { - cimg_forX(*this,x) { - const unsigned char val = (unsigned char)*(ptr_r++); - std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile); - } - cimg::fwrite(align_buf,align,nfile); - ptr_r-=2*_width; - } - } break; - case 2 : { - cimg_forY(*this,y) { - cimg_forX(*this,x) { - std::fputc(0,nfile); - std::fputc((unsigned char)(*(ptr_g++)),nfile); - std::fputc((unsigned char)(*(ptr_r++)),nfile); - } - cimg::fwrite(align_buf,align,nfile); - ptr_r-=2*_width; ptr_g-=2*_width; - } - } break; - default : { - cimg_forY(*this,y) { - cimg_forX(*this,x) { - std::fputc((unsigned char)(*(ptr_b++)),nfile); - std::fputc((unsigned char)(*(ptr_g++)),nfile); - std::fputc((unsigned char)(*(ptr_r++)),nfile); - } - cimg::fwrite(align_buf,align,nfile); - ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width; - } - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a JPEG file. - /** - \param filename Filename, as a C-string. - \param quality Image quality (in %) - **/ - const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const { - return _save_jpeg(0,filename,quality); - } - - //! Save image as a JPEG file \overloading. - const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const { - return _save_jpeg(file,0,quality); - } - - const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_jpeg(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - -#ifndef cimg_use_jpeg - if (!file) return save_other(filename,quality); - else throw CImgIOException(_cimg_instance - "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.", - cimg_instance); -#else - unsigned int dimbuf = 0; - J_COLOR_SPACE colortype = JCS_RGB; - - switch (_spectrum) { - case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break; - case 2 : dimbuf = 3; colortype = JCS_RGB; break; - case 3 : dimbuf = 3; colortype = JCS_RGB; break; - default : dimbuf = 4; colortype = JCS_CMYK; break; - } - - // Call libjpeg functions - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - jpeg_stdio_dest(&cinfo,nfile); - cinfo.image_width = _width; - cinfo.image_height = _height; - cinfo.input_components = dimbuf; - cinfo.in_color_space = colortype; - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); - jpeg_start_compress(&cinfo,TRUE); - - JSAMPROW row_pointer[1]; - CImg<ucharT> buffer(_width*dimbuf); - - while (cinfo.next_scanline<cinfo.image_height) { - unsigned char *ptrd = buffer._data; - - // Fill pixel buffer - switch (_spectrum) { - case 1 : { // Greyscale images - const T *ptr_g = data(0, cinfo.next_scanline); - for (unsigned int b = 0; b<cinfo.image_width; b++) - *(ptrd++) = (unsigned char)*(ptr_g++); - } break; - case 2 : { // RG images - const T *ptr_r = data(0,cinfo.next_scanline,0,0), - *ptr_g = data(0,cinfo.next_scanline,0,1); - for (unsigned int b = 0; b<cinfo.image_width; ++b) { - *(ptrd++) = (unsigned char)*(ptr_r++); - *(ptrd++) = (unsigned char)*(ptr_g++); - *(ptrd++) = 0; - } - } break; - case 3 : { // RGB images - const T *ptr_r = data(0,cinfo.next_scanline,0,0), - *ptr_g = data(0,cinfo.next_scanline,0,1), - *ptr_b = data(0,cinfo.next_scanline,0,2); - for (unsigned int b = 0; b<cinfo.image_width; ++b) { - *(ptrd++) = (unsigned char)*(ptr_r++); - *(ptrd++) = (unsigned char)*(ptr_g++); - *(ptrd++) = (unsigned char)*(ptr_b++); - } - } break; - default : { // CMYK images - const T *ptr_r = data(0,cinfo.next_scanline,0,0), - *ptr_g = data(0,cinfo.next_scanline,0,1), - *ptr_b = data(0,cinfo.next_scanline,0,2), - *ptr_a = data(0,cinfo.next_scanline,0,3); - for (unsigned int b = 0; b<cinfo.image_width; ++b) { - *(ptrd++) = (unsigned char)*(ptr_r++); - *(ptrd++) = (unsigned char)*(ptr_g++); - *(ptrd++) = (unsigned char)*(ptr_b++); - *(ptrd++) = (unsigned char)*(ptr_a++); - } - } - } - *row_pointer = buffer._data; - jpeg_write_scanlines(&cinfo,row_pointer,1); - } - jpeg_finish_compress(&cinfo); - if (!file) cimg::fclose(nfile); - jpeg_destroy_compress(&cinfo); - return *this; -#endif - } - - //! Save image, using built-in ImageMagick++ library. - /** - \param filename Filename, as a C-string. - \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible. - **/ - const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_magick(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - -#ifdef cimg_use_magick - double stmin, stmax = (double)max_min(stmin); - if (_depth>1) - cimg::warn(_cimg_instance - "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.", - cimg_instance, - filename); - - if (_spectrum>3) - cimg::warn(_cimg_instance - "save_magick(): Instance is multispectral, only the three first channels will be " - "saved in file '%s'.", - cimg_instance, - filename); - - if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) - cimg::warn(_cimg_instance - "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", - cimg_instance, - filename,stmin,stmax); - - Magick::Image image(Magick::Geometry(_width,_height),"black"); - image.type(Magick::TrueColorType); - image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8)); - const T - *ptr_r = data(0,0,0,0), - *ptr_g = _spectrum>1?data(0,0,0,1):0, - *ptr_b = _spectrum>2?data(0,0,0,2):0; - Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height); - switch (_spectrum) { - case 1 : // Scalar images - for (ulongT off = (ulongT)_width*_height; off; --off) { - pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++); - ++pixels; - } - break; - case 2 : // RG images - for (ulongT off = (ulongT)_width*_height; off; --off) { - pixels->red = (Magick::Quantum)*(ptr_r++); - pixels->green = (Magick::Quantum)*(ptr_g++); - pixels->blue = 0; ++pixels; - } - break; - default : // RGB images - for (ulongT off = (ulongT)_width*_height; off; --off) { - pixels->red = (Magick::Quantum)*(ptr_r++); - pixels->green = (Magick::Quantum)*(ptr_g++); - pixels->blue = (Magick::Quantum)*(ptr_b++); - ++pixels; - } - } - image.syncPixels(); - image.write(filename); - return *this; -#else - cimg::unused(bytes_per_pixel); - throw CImgIOException(_cimg_instance - "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.", - cimg_instance, - filename); -#endif - } - - //! Save image as a PNG file. - /** - \param filename Filename, as a C-string. - \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible. - **/ - const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const { - return _save_png(0,filename,bytes_per_pixel); - } - - //! Save image as a PNG file \overloading. - const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { - return _save_png(file,0,bytes_per_pixel); - } - - const CImg<T>& _save_png(std::FILE *const file, const char *const filename, - const unsigned int bytes_per_pixel=0) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_png(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - -#ifndef cimg_use_png - cimg::unused(bytes_per_pixel); - if (!file) return save_other(filename); - else throw CImgIOException(_cimg_instance - "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.", - cimg_instance); -#else - -#if defined __GNUC__ - const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning - std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); - volatile double stmin, stmax = (double)max_min(stmin); -#else - const char *nfilename = filename; - std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb"); - double stmin, stmax = (double)max_min(stmin); -#endif - - if (_depth>1) - cimg::warn(_cimg_instance - "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.", - cimg_instance, - filename); - - if (_spectrum>4) - cimg::warn(_cimg_instance - "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.", - cimg_instance, - filename); - - if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) - cimg::warn(_cimg_instance - "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", - cimg_instance, - filename,stmin,stmax); - - // Setup PNG structures for write - png_voidp user_error_ptr = 0; - png_error_ptr user_error_fn = 0, user_warning_fn = 0; - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, - user_warning_fn); - if (!png_ptr){ - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_write_struct(&png_ptr,(png_infopp)0); - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, &info_ptr); - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - png_init_io(png_ptr, nfile); - - const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8); - - int color_type; - switch (spectrum()) { - case 1 : color_type = PNG_COLOR_TYPE_GRAY; break; - case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; - case 3 : color_type = PNG_COLOR_TYPE_RGB; break; - default : color_type = PNG_COLOR_TYPE_RGB_ALPHA; - } - const int interlace_type = PNG_INTERLACE_NONE; - const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; - const int filter_method = PNG_FILTER_TYPE_DEFAULT; - png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method); - png_write_info(png_ptr,info_ptr); - const int byte_depth = bit_depth>>3; - const int numChan = spectrum()>4?4:spectrum(); - const int pixel_bit_depth_flag = numChan * (bit_depth - 1); - - // Allocate Memory for Image Save and Fill pixel data - png_bytep *const imgData = new png_byte*[_height]; - for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width]; - const T *pC0 = data(0,0,0,0); - switch (pixel_bit_depth_flag) { - case 7 : { // Gray 8-bit - cimg_forY(*this,y) { - unsigned char *ptrd = imgData[y]; - cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++); - } - } break; - case 14 : { // Gray w/ Alpha 8-bit - const T *pC1 = data(0,0,0,1); - cimg_forY(*this,y) { - unsigned char *ptrd = imgData[y]; - cimg_forX(*this,x) { - *(ptrd++) = (unsigned char)*(pC0++); - *(ptrd++) = (unsigned char)*(pC1++); - } - } - } break; - case 21 : { // RGB 8-bit - const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); - cimg_forY(*this,y) { - unsigned char *ptrd = imgData[y]; - cimg_forX(*this,x) { - *(ptrd++) = (unsigned char)*(pC0++); - *(ptrd++) = (unsigned char)*(pC1++); - *(ptrd++) = (unsigned char)*(pC2++); - } - } - } break; - case 28 : { // RGB x/ Alpha 8-bit - const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); - cimg_forY(*this,y){ - unsigned char *ptrd = imgData[y]; - cimg_forX(*this,x){ - *(ptrd++) = (unsigned char)*(pC0++); - *(ptrd++) = (unsigned char)*(pC1++); - *(ptrd++) = (unsigned char)*(pC2++); - *(ptrd++) = (unsigned char)*(pC3++); - } - } - } break; - case 15 : { // Gray 16-bit - cimg_forY(*this,y){ - unsigned short *ptrd = (unsigned short*)(imgData[y]); - cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++); - if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width); - } - } break; - case 30 : { // Gray w/ Alpha 16-bit - const T *pC1 = data(0,0,0,1); - cimg_forY(*this,y){ - unsigned short *ptrd = (unsigned short*)(imgData[y]); - cimg_forX(*this,x) { - *(ptrd++) = (unsigned short)*(pC0++); - *(ptrd++) = (unsigned short)*(pC1++); - } - if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width); - } - } break; - case 45 : { // RGB 16-bit - const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); - cimg_forY(*this,y) { - unsigned short *ptrd = (unsigned short*)(imgData[y]); - cimg_forX(*this,x) { - *(ptrd++) = (unsigned short)*(pC0++); - *(ptrd++) = (unsigned short)*(pC1++); - *(ptrd++) = (unsigned short)*(pC2++); - } - if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width); - } - } break; - case 60 : { // RGB w/ Alpha 16-bit - const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); - cimg_forY(*this,y) { - unsigned short *ptrd = (unsigned short*)(imgData[y]); - cimg_forX(*this,x) { - *(ptrd++) = (unsigned short)*(pC0++); - *(ptrd++) = (unsigned short)*(pC1++); - *(ptrd++) = (unsigned short)*(pC2++); - *(ptrd++) = (unsigned short)*(pC3++); - } - if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width); - } - } break; - default : - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimg_instance - "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", - cimg_instance, - nfilename?nfilename:"(FILE*)"); - } - png_write_image(png_ptr,imgData); - png_write_end(png_ptr,info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - - // Deallocate Image Write Memory - cimg_forY(*this,n) delete[] imgData[n]; - delete[] imgData; - - if (!file) cimg::fclose(nfile); - return *this; -#endif - } - - //! Save image as a PNM file. - /** - \param filename Filename, as a C-string. - \param bytes_per_pixel Force the number of bytes per pixels for the saving. - **/ - const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const { - return _save_pnm(0,filename,bytes_per_pixel); - } - - //! Save image as a PNM file \overloading. - const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { - return _save_pnm(file,0,bytes_per_pixel); - } - - const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename, - const unsigned int bytes_per_pixel=0) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_pnm(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - - double stmin, stmax = (double)max_min(stmin); - if (_depth>1) - cimg::warn(_cimg_instance - "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - if (_spectrum>3) - cimg::warn(_cimg_instance - "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) - cimg::warn(_cimg_instance - "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", - cimg_instance, - stmin,stmax,filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - const T - *ptr_r = data(0,0,0,0), - *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, - *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; - const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL))); - - std::fprintf(nfile,"P%c\n%u %u\n%u\n", - (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535)); - - switch (_spectrum) { - case 1 : { // Scalar image - if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits - CImg<ucharT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size); - unsigned char *ptrd = buf._data; - for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++); - cimg::fwrite(buf._data,N,nfile); - to_write-=N; - } - } else { // Binary PGM 16 bits - CImg<ushortT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size); - unsigned short *ptrd = buf._data; - for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++); - if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); - cimg::fwrite(buf._data,N,nfile); - to_write-=N; - } - } - } break; - case 2 : { // RG image - if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits - CImg<ucharT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size/3); - unsigned char *ptrd = buf._data; - for (ulongT i = N; i>0; --i) { - *(ptrd++) = (unsigned char)*(ptr_r++); - *(ptrd++) = (unsigned char)*(ptr_g++); - *(ptrd++) = 0; - } - cimg::fwrite(buf._data,3*N,nfile); - to_write-=N; - } - } else { // Binary PPM 16 bits - CImg<ushortT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size/3); - unsigned short *ptrd = buf._data; - for (ulongT i = N; i>0; --i) { - *(ptrd++) = (unsigned short)*(ptr_r++); - *(ptrd++) = (unsigned short)*(ptr_g++); - *(ptrd++) = 0; - } - if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); - cimg::fwrite(buf._data,3*N,nfile); - to_write-=N; - } - } - } break; - default : { // RGB image - if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits - CImg<ucharT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size/3); - unsigned char *ptrd = buf._data; - for (ulongT i = N; i>0; --i) { - *(ptrd++) = (unsigned char)*(ptr_r++); - *(ptrd++) = (unsigned char)*(ptr_g++); - *(ptrd++) = (unsigned char)*(ptr_b++); - } - cimg::fwrite(buf._data,3*N,nfile); - to_write-=N; - } - } else { // Binary PPM 16 bits - CImg<ushortT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size/3); - unsigned short *ptrd = buf._data; - for (ulongT i = N; i>0; --i) { - *(ptrd++) = (unsigned short)*(ptr_r++); - *(ptrd++) = (unsigned short)*(ptr_g++); - *(ptrd++) = (unsigned short)*(ptr_b++); - } - if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); - cimg::fwrite(buf._data,3*N,nfile); - to_write-=N; - } - } - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a PNK file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_pnk(const char *const filename) const { - return _save_pnk(0,filename); - } - - //! Save image as a PNK file \overloading. - const CImg<T>& save_pnk(std::FILE *const file) const { - return _save_pnk(file,0); - } - - const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_pnk(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if (_spectrum>1) - cimg::warn(_cimg_instance - "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth); - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - const T *ptr = data(0,0,0,0); - - if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file - _save_pnm(file,filename,0); - else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D - std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth); - CImg<ucharT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size); - unsigned char *ptrd = buf._data; - for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++); - cimg::fwrite(buf._data,N,nfile); - to_write-=N; - } - } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3D - if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max()); - else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max()); - CImg<intT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size); - int *ptrd = buf._data; - for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++); - cimg::fwrite(buf._data,N,nfile); - to_write-=N; - } - } else { // Save as P9: Binary float-valued 3D - if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max()); - else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max()); - CImg<floatT> buf((unsigned int)buf_size); - for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,buf_size); - float *ptrd = buf._data; - for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++); - cimg::fwrite(buf._data,N,nfile); - to_write-=N; - } - } - - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a PFM file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_pfm(const char *const filename) const { - get_mirror('y')._save_pfm(0,filename); - return *this; - } - - //! Save image as a PFM file \overloading. - const CImg<T>& save_pfm(std::FILE *const file) const { - get_mirror('y')._save_pfm(file,0); - return *this; - } - - const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_pfm(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - if (_spectrum>3) - cimg::warn(_cimg_instance - "save_pfm(): image instance is multispectral, only the three first channels will be saved " - "in file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - const T - *ptr_r = data(0,0,0,0), - *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, - *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; - const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3)); - - std::fprintf(nfile,"P%c\n%u %u\n1.0\n", - (_spectrum==1?'f':'F'),_width,_height); - - switch (_spectrum) { - case 1 : { // Scalar image - CImg<floatT> buf(buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size); - float *ptrd = buf._data; - for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++); - if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); - cimg::fwrite(buf._data,N,nfile); - to_write-=N; - } - } break; - case 2 : { // RG image - CImg<floatT> buf(buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const unsigned int N = std::min((unsigned int)to_write,buf_size/3); - float *ptrd = buf._data; - for (ulongT i = N; i>0; --i) { - *(ptrd++) = (float)*(ptr_r++); - *(ptrd++) = (float)*(ptr_g++); - *(ptrd++) = 0; - } - if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); - cimg::fwrite(buf._data,3*N,nfile); - to_write-=N; - } - } break; - default : { // RGB image - CImg<floatT> buf(buf_size); - for (longT to_write = (longT)width()*height(); to_write>0; ) { - const unsigned int N = std::min((unsigned int)to_write,buf_size/3); - float *ptrd = buf._data; - for (ulongT i = N; i>0; --i) { - *(ptrd++) = (float)*(ptr_r++); - *(ptrd++) = (float)*(ptr_g++); - *(ptrd++) = (float)*(ptr_b++); - } - if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); - cimg::fwrite(buf._data,3*N,nfile); - to_write-=N; - } - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a RGB file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_rgb(const char *const filename) const { - return _save_rgb(0,filename); - } - - //! Save image as a RGB file \overloading. - const CImg<T>& save_rgb(std::FILE *const file) const { - return _save_rgb(file,0); - } - - const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_rgb(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if (_spectrum!=3) - cimg::warn(_cimg_instance - "save_rgb(): image instance has not exactly 3 channels, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - const ulongT wh = (ulongT)_width*_height; - unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer; - const T - *ptr1 = data(0,0,0,0), - *ptr2 = _spectrum>1?data(0,0,0,1):0, - *ptr3 = _spectrum>2?data(0,0,0,2):0; - switch (_spectrum) { - case 1 : { // Scalar image - for (ulongT k = 0; k<wh; ++k) { - const unsigned char val = (unsigned char)*(ptr1++); - *(nbuffer++) = val; - *(nbuffer++) = val; - *(nbuffer++) = val; - } - } break; - case 2 : { // RG image - for (ulongT k = 0; k<wh; ++k) { - *(nbuffer++) = (unsigned char)(*(ptr1++)); - *(nbuffer++) = (unsigned char)(*(ptr2++)); - *(nbuffer++) = 0; - } - } break; - default : { // RGB image - for (ulongT k = 0; k<wh; ++k) { - *(nbuffer++) = (unsigned char)(*(ptr1++)); - *(nbuffer++) = (unsigned char)(*(ptr2++)); - *(nbuffer++) = (unsigned char)(*(ptr3++)); - } - } - } - cimg::fwrite(buffer,3*wh,nfile); - if (!file) cimg::fclose(nfile); - delete[] buffer; - return *this; - } - - //! Save image as a RGBA file. - /** - \param filename Filename, as a C-string. - **/ - const CImg<T>& save_rgba(const char *const filename) const { - return _save_rgba(0,filename); - } - - //! Save image as a RGBA file \overloading. - const CImg<T>& save_rgba(std::FILE *const file) const { - return _save_rgba(file,0); - } - - const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_rgba(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if (_spectrum!=4) - cimg::warn(_cimg_instance - "save_rgba(): image instance has not exactly 4 channels, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - const ulongT wh = (ulongT)_width*_height; - unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer; - const T - *ptr1 = data(0,0,0,0), - *ptr2 = _spectrum>1?data(0,0,0,1):0, - *ptr3 = _spectrum>2?data(0,0,0,2):0, - *ptr4 = _spectrum>3?data(0,0,0,3):0; - switch (_spectrum) { - case 1 : { // Scalar images - for (ulongT k = 0; k<wh; ++k) { - const unsigned char val = (unsigned char)*(ptr1++); - *(nbuffer++) = val; - *(nbuffer++) = val; - *(nbuffer++) = val; - *(nbuffer++) = 255; - } - } break; - case 2 : { // RG images - for (ulongT k = 0; k<wh; ++k) { - *(nbuffer++) = (unsigned char)(*(ptr1++)); - *(nbuffer++) = (unsigned char)(*(ptr2++)); - *(nbuffer++) = 0; - *(nbuffer++) = 255; - } - } break; - case 3 : { // RGB images - for (ulongT k = 0; k<wh; ++k) { - *(nbuffer++) = (unsigned char)(*(ptr1++)); - *(nbuffer++) = (unsigned char)(*(ptr2++)); - *(nbuffer++) = (unsigned char)(*(ptr3++)); - *(nbuffer++) = 255; - } - } break; - default : { // RGBA images - for (ulongT k = 0; k<wh; ++k) { - *(nbuffer++) = (unsigned char)(*(ptr1++)); - *(nbuffer++) = (unsigned char)(*(ptr2++)); - *(nbuffer++) = (unsigned char)(*(ptr3++)); - *(nbuffer++) = (unsigned char)(*(ptr4++)); - } - } - } - cimg::fwrite(buffer,4*wh,nfile); - if (!file) cimg::fclose(nfile); - delete[] buffer; - return *this; - } - - //! Save image as a TIFF file. - /** - \param filename Filename, as a C-string. - \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>. - \param voxel_size Voxel size, to be stored in the filename. - \param description Description, to be stored in the filename. - \param use_bigtiff Allow to save big tiff files (>4Gb). - \note - - libtiff support is enabled by defining the precompilation - directive \c cimg_use_tif. - - When libtiff is enabled, 2D and 3D (multipage) several - channel per pixel are supported for - <tt>char,uchar,short,ushort,float</tt> and \c double pixel types. - - If \c cimg_use_tif is not defined at compile time the - function uses CImg<T>&save_other(const char*). - **/ - const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0, - const float *const voxel_size=0, const char *const description=0, - const bool use_bigtiff=true) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_tiff(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - -#ifdef cimg_use_tiff - const bool - _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images - TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); - if (tif) { - cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description); - TIFFClose(tif); - } else throw CImgIOException(_cimg_instance - "save_tiff(): Failed to open file '%s' for writing.", - cimg_instance, - filename); - return *this; -#else - cimg::unused(compression_type,voxel_size,description,use_bigtiff); - return save_other(filename); -#endif - } - -#ifdef cimg_use_tiff - -#define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \ - const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); } - - // [internal] Save a plane into a tiff file - template<typename t> - const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t, - const unsigned int compression_type, const float *const voxel_size, - const char *const description) const { - if (is_empty() || !tif || pixel_t) return *this; - const char *const filename = TIFFFileName(tif); - uint32 rowsperstrip = (uint32)-1; - uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric; - if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; - else photometric = PHOTOMETRIC_MINISBLACK; - TIFFSetDirectory(tif,directory); - TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width); - TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height); - if (voxel_size) { - const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2]; - TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE); - TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx); - TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy); - CImg<charT> s_description(256); - cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz); - TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data()); - } - if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description); - TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); - TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp); - if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); - else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1); - else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2); - double valm, valM = max_min(valm); - TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm); - TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM); - TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp); - TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); - TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric); - TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG: - compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE); - rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); - TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); - TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); - TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); - - t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); - if (buf) { - for (unsigned int row = 0; row<_height; row+=rowsperstrip) { - uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip); - tstrip_t strip = TIFFComputeStrip(tif,row,0); - tsize_t i = 0; - for (unsigned int rr = 0; rr<nrow; ++rr) - for (unsigned int cc = 0; cc<_width; ++cc) - for (unsigned int vv = 0; vv<spp; ++vv) - buf[i++] = (t)(*this)(cc,row + rr,z,vv); - if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0) - throw CImgIOException(_cimg_instance - "save_tiff(): Invalid strip writing when saving file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - } - _TIFFfree(buf); - } - TIFFWriteDirectory(tif); - return *this; - } - - const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, - const unsigned int compression_type, const float *const voxel_size, - const char *const description) const { - _cimg_save_tiff("bool",unsigned char,compression_type); - _cimg_save_tiff("unsigned char",unsigned char,compression_type); - _cimg_save_tiff("char",char,compression_type); - _cimg_save_tiff("unsigned short",unsigned short,compression_type); - _cimg_save_tiff("short",short,compression_type); - _cimg_save_tiff("unsigned int",unsigned int,compression_type); - _cimg_save_tiff("int",int,compression_type); - _cimg_save_tiff("unsigned int64",unsigned int,compression_type); - _cimg_save_tiff("int64",int,compression_type); - _cimg_save_tiff("float",float,compression_type); - _cimg_save_tiff("double",float,compression_type); - const char *const filename = TIFFFileName(tif); - throw CImgInstanceException(_cimg_instance - "save_tiff(): Unsupported pixel type '%s' for file '%s'.", - cimg_instance, - pixel_type(),filename?filename:"(FILE*)"); - return *this; - } -#endif - - //! Save image as a MINC2 file. - /** - \param filename Filename, as a C-string. - \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from. - **/ - const CImg<T>& save_minc2(const char *const filename, - const char *const imitate_file=0) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_minc2(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - -#ifndef cimg_use_minc2 - cimg::unused(imitate_file); - return save_other(filename); -#else - minc::minc_1_writer wtr; - if (imitate_file) - wtr.open(filename, imitate_file); - else { - minc::minc_info di; - if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X)); - if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y)); - if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z)); - if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME)); - wtr.open(filename,di,1,NC_FLOAT,0); - } - if (cimg::type<T>::string()==cimg::type<unsigned char>::string()) - wtr.setup_write_byte(); - else if (cimg::type<T>::string()==cimg::type<int>::string()) - wtr.setup_write_int(); - else if (cimg::type<T>::string()==cimg::type<double>::string()) - wtr.setup_write_double(); - else - wtr.setup_write_float(); - minc::save_standard_volume(wtr, this->_data); - return *this; -#endif - } - - //! Save image as an ANALYZE7.5 or NIFTI file. - /** - \param filename Filename, as a C-string. - \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions. - **/ - const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_analyze(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - - std::FILE *file; - CImg<charT> hname(1024), iname(1024); - const char *const ext = cimg::split_filename(filename); - short datatype = -1; - if (!*ext) { - cimg_snprintf(hname,hname._width,"%s.hdr",filename); - cimg_snprintf(iname,iname._width,"%s.img",filename); - } - if (!cimg::strncasecmp(ext,"hdr",3)) { - std::strcpy(hname,filename); - std::strncpy(iname,filename,iname._width - 1); - cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); - } - if (!cimg::strncasecmp(ext,"img",3)) { - std::strcpy(hname,filename); - std::strncpy(iname,filename,iname._width - 1); - cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); - } - if (!cimg::strncasecmp(ext,"nii",3)) { - std::strncpy(hname,filename,hname._width - 1); *iname = 0; - } - - CImg<charT> header(*iname?348:352,1,1,1,0); - int *const iheader = (int*)header._data; - *iheader = 348; - std::strcpy(header._data + 4,"CImg"); - std::strcpy(header._data + 14," "); - ((short*)&(header[36]))[0] = 4096; - ((char*)&(header[38]))[0] = 114; - ((short*)&(header[40]))[0] = 4; - ((short*)&(header[40]))[1] = (short)_width; - ((short*)&(header[40]))[2] = (short)_height; - ((short*)&(header[40]))[3] = (short)_depth; - ((short*)&(header[40]))[4] = (short)_spectrum; - if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; - if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; - if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; - if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; - if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; - if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; - if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; - if (datatype<0) - throw CImgIOException(_cimg_instance - "save_analyze(): Unsupported pixel type '%s' for file '%s'.", - cimg_instance, - pixel_type(),filename); - - ((short*)&(header[70]))[0] = datatype; - ((short*)&(header[72]))[0] = sizeof(T); - ((float*)&(header[108]))[0] = (float)(*iname?0:header.width()); - ((float*)&(header[112]))[0] = 1; - ((float*)&(header[76]))[0] = 0; - if (voxel_size) { - ((float*)&(header[76]))[1] = voxel_size[0]; - ((float*)&(header[76]))[2] = voxel_size[1]; - ((float*)&(header[76]))[3] = voxel_size[2]; - } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1; - file = cimg::fopen(hname,"wb"); - cimg::fwrite(header._data,header.width(),file); - if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } - cimg::fwrite(_data,size(),file); - cimg::fclose(file); - return *this; - } - - //! Save image as a .cimg file. - /** - \param filename Filename, as a C-string. - \param is_compressed Tells if the file contains compressed image data. - **/ - const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const { - CImgList<T>(*this,true).save_cimg(filename,is_compressed); - return *this; - } - - //! Save image as a .cimg file \overloading. - const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const { - CImgList<T>(*this,true).save_cimg(file,is_compressed); - return *this; - } - - //! Save image as a sub-image into an existing .cimg file. - /** - \param filename Filename, as a C-string. - \param n0 Index of the image inside the file. - \param x0 X-coordinate of the sub-image location. - \param y0 Y-coordinate of the sub-image location. - \param z0 Z-coordinate of the sub-image location. - \param c0 C-coordinate of the sub-image location. - **/ - const CImg<T>& save_cimg(const char *const filename, - const unsigned int n0, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0) const { - CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0); - return *this; - } - - //! Save image as a sub-image into an existing .cimg file \overloading. - const CImg<T>& save_cimg(std::FILE *const file, - const unsigned int n0, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0) const { - CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0); - return *this; - } - - //! Save blank image as a .cimg file. - /** - \param filename Filename, as a C-string. - \param dx Width of the image. - \param dy Height of the image. - \param dz Depth of the image. - \param dc Number of channels of the image. - \note - - All pixel values of the saved image are set to \c 0. - - Use this method to save large images without having to instanciate and allocate them. - **/ - static void save_empty_cimg(const char *const filename, - const unsigned int dx, const unsigned int dy=1, - const unsigned int dz=1, const unsigned int dc=1) { - return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc); - } - - //! Save blank image as a .cimg file \overloading. - /** - Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int) - with a file stream argument instead of a filename string. - **/ - static void save_empty_cimg(std::FILE *const file, - const unsigned int dx, const unsigned int dy=1, - const unsigned int dz=1, const unsigned int dc=1) { - return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc); - } - - //! Save image as an INRIMAGE-4 file. - /** - \param filename Filename, as a C-string. - \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions. - **/ - const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const { - return _save_inr(0,filename,voxel_size); - } - - //! Save image as an INRIMAGE-4 file \overloading. - const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const { - return _save_inr(file,0,voxel_size); - } - - const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_inr(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - - int inrpixsize = -1; - const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; - if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { - inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; - } - if (!cimg::strcasecmp(pixel_type(),"char")) { - inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; - } - if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { - inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; - } - if (!cimg::strcasecmp(pixel_type(),"short")) { - inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; - } - if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { - inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; - } - if (!cimg::strcasecmp(pixel_type(),"int")) { - inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; - } - if (!cimg::strcasecmp(pixel_type(),"float")) { - inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; - } - if (!cimg::strcasecmp(pixel_type(),"double")) { - inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; - } - if (inrpixsize<=0) - throw CImgIOException(_cimg_instance - "save_inr(): Unsupported pixel type '%s' for file '%s'", - cimg_instance, - pixel_type(),filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - CImg<charT> header(257); - int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", - _width,_height,_depth,_spectrum); - if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n", - voxel_size[0],voxel_size[1],voxel_size[2]); - err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); - std::memset(header._data + err,'\n',252 - err); - std::memcpy(header._data + 252,"##}\n",4); - cimg::fwrite(header._data,256,nfile); - cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile); - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as an OpenEXR file. - /** - \param filename Filename, as a C-string. - \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>. - **/ - const CImg<T>& save_exr(const char *const filename) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_exr(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.", - cimg_instance, - filename); - -#ifndef cimg_use_openexr - return save_other(filename); -#else - Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba; - switch (_spectrum) { - case 1 : { // Grayscale image - for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) { - rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++)); - rgba.a = (half)1; - *(ptrd++) = rgba; - } - } break; - case 2 : { // RG image - for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), - *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) { - rgba.r = (half)(*(ptr_r++)); - rgba.g = (half)(*(ptr_g++)); - rgba.b = (half)0; - rgba.a = (half)1; - *(ptrd++) = rgba; - } - } break; - case 3 : { // RGB image - for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), - *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) { - rgba.r = (half)(*(ptr_r++)); - rgba.g = (half)(*(ptr_g++)); - rgba.b = (half)(*(ptr_b++)); - rgba.a = (half)1; - *(ptrd++) = rgba; - } - } break; - default : { // RGBA image - for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3), - *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) { - rgba.r = (half)(*(ptr_r++)); - rgba.g = (half)(*(ptr_g++)); - rgba.b = (half)(*(ptr_b++)); - rgba.a = (half)(*(ptr_a++)); - *(ptrd++) = rgba; - } - } break; - } - Imf::RgbaOutputFile outFile(filename,_width,_height, - _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3? - Imf::WRITE_RGB:Imf::WRITE_RGBA); - outFile.setFrameBuffer(ptrd0,1,_width); - outFile.writePixels(_height); - delete[] ptrd0; - return *this; -#endif - } - - //! Save image as a Pandore-5 file. - /** - \param filename Filename, as a C-string. - \param colorspace Colorspace data field in output file - (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a> - for more information). - **/ - const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const { - return _save_pandore(0,filename,colorspace); - } - - //! Save image as a Pandore-5 file \overloading. - /** - Same as save_pandore(const char *,unsigned int) const - with a file stream argument instead of a filename string. - **/ - const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const { - return _save_pandore(file,0,colorspace); - } - - unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const { - unsigned int nbdims = 0; - if (id==2 || id==3 || id==4) { - dims[0] = 1; dims[1] = _width; nbdims = 2; - } - if (id==5 || id==6 || id==7) { - dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3; - } - if (id==8 || id==9 || id==10) { - dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; - } - if (id==16 || id==17 || id==18) { - dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4; - } - if (id==19 || id==20 || id==21) { - dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; - } - if (id==22 || id==23 || id==25) { - dims[0] = _spectrum; dims[1] = _width; nbdims = 2; - } - if (id==26 || id==27 || id==29) { - dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; - } - if (id==30 || id==31 || id==33) { - dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; - } - return nbdims; - } - - const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename, - const unsigned int colorspace) const { - -#define __cimg_save_pandore_case(dtype) \ - dtype *buffer = new dtype[size()]; \ - const T *ptrs = _data; \ - cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \ - buffer-=size(); \ - cimg::fwrite(buffer,size(),nfile); \ - delete[] buffer - -#define _cimg_save_pandore_case(sy,sz,sv,stype,id) \ - if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \ - (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \ - unsigned int *iheader = (unsigned int*)(header + 12); \ - nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ - cimg::fwrite(header,36,nfile); \ - if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \ - for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ - else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \ - for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ - else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \ - for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ - else throw CImgIOException(_cimg_instance \ - "save_pandore(): Unsupported datatype for file '%s'.",\ - cimg_instance, \ - filename?filename:"(FILE*)"); \ - if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ - __cimg_save_pandore_case(unsigned char); \ - } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ - if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \ - else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \ - else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \ - else throw CImgIOException(_cimg_instance \ - "save_pandore(): Unsupported datatype for file '%s'.",\ - cimg_instance, \ - filename?filename:"(FILE*)"); \ - } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ - if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \ - else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \ - else throw CImgIOException(_cimg_instance \ - "save_pandore(): Unsupported datatype for file '%s'.",\ - cimg_instance, \ - filename?filename:"(FILE*)"); \ - } \ - saved = true; \ - } - - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_pandore(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, - 0,0,0,0,'C','I','m','g',0,0,0,0,0, - 'N','o',' ','d','a','t','e',0,0,0,0 }; - unsigned int nbdims, dims[5] = { 0 }; - bool saved = false; - _cimg_save_pandore_case(1,1,1,"unsigned char",2); - _cimg_save_pandore_case(1,1,1,"char",3); - _cimg_save_pandore_case(1,1,1,"unsigned short",3); - _cimg_save_pandore_case(1,1,1,"short",3); - _cimg_save_pandore_case(1,1,1,"unsigned int",3); - _cimg_save_pandore_case(1,1,1,"int",3); - _cimg_save_pandore_case(1,1,1,"unsigned int64",3); - _cimg_save_pandore_case(1,1,1,"int64",3); - _cimg_save_pandore_case(1,1,1,"float",4); - _cimg_save_pandore_case(1,1,1,"double",4); - - _cimg_save_pandore_case(0,1,1,"unsigned char",5); - _cimg_save_pandore_case(0,1,1,"char",6); - _cimg_save_pandore_case(0,1,1,"unsigned short",6); - _cimg_save_pandore_case(0,1,1,"short",6); - _cimg_save_pandore_case(0,1,1,"unsigned int",6); - _cimg_save_pandore_case(0,1,1,"int",6); - _cimg_save_pandore_case(0,1,1,"unsigned int64",6); - _cimg_save_pandore_case(0,1,1,"int64",6); - _cimg_save_pandore_case(0,1,1,"float",7); - _cimg_save_pandore_case(0,1,1,"double",7); - - _cimg_save_pandore_case(0,0,1,"unsigned char",8); - _cimg_save_pandore_case(0,0,1,"char",9); - _cimg_save_pandore_case(0,0,1,"unsigned short",9); - _cimg_save_pandore_case(0,0,1,"short",9); - _cimg_save_pandore_case(0,0,1,"unsigned int",9); - _cimg_save_pandore_case(0,0,1,"int",9); - _cimg_save_pandore_case(0,0,1,"unsigned int64",9); - _cimg_save_pandore_case(0,0,1,"int64",9); - _cimg_save_pandore_case(0,0,1,"float",10); - _cimg_save_pandore_case(0,0,1,"double",10); - - _cimg_save_pandore_case(0,1,3,"unsigned char",16); - _cimg_save_pandore_case(0,1,3,"char",17); - _cimg_save_pandore_case(0,1,3,"unsigned short",17); - _cimg_save_pandore_case(0,1,3,"short",17); - _cimg_save_pandore_case(0,1,3,"unsigned int",17); - _cimg_save_pandore_case(0,1,3,"int",17); - _cimg_save_pandore_case(0,1,3,"unsigned int64",17); - _cimg_save_pandore_case(0,1,3,"int64",17); - _cimg_save_pandore_case(0,1,3,"float",18); - _cimg_save_pandore_case(0,1,3,"double",18); - - _cimg_save_pandore_case(0,0,3,"unsigned char",19); - _cimg_save_pandore_case(0,0,3,"char",20); - _cimg_save_pandore_case(0,0,3,"unsigned short",20); - _cimg_save_pandore_case(0,0,3,"short",20); - _cimg_save_pandore_case(0,0,3,"unsigned int",20); - _cimg_save_pandore_case(0,0,3,"int",20); - _cimg_save_pandore_case(0,0,3,"unsigned int64",20); - _cimg_save_pandore_case(0,0,3,"int64",20); - _cimg_save_pandore_case(0,0,3,"float",21); - _cimg_save_pandore_case(0,0,3,"double",21); - - _cimg_save_pandore_case(1,1,0,"unsigned char",22); - _cimg_save_pandore_case(1,1,0,"char",23); - _cimg_save_pandore_case(1,1,0,"unsigned short",23); - _cimg_save_pandore_case(1,1,0,"short",23); - _cimg_save_pandore_case(1,1,0,"unsigned int",23); - _cimg_save_pandore_case(1,1,0,"int",23); - _cimg_save_pandore_case(1,1,0,"unsigned int64",23); - _cimg_save_pandore_case(1,1,0,"int64",23); - _cimg_save_pandore_case(1,1,0,"float",25); - _cimg_save_pandore_case(1,1,0,"double",25); - - _cimg_save_pandore_case(0,1,0,"unsigned char",26); - _cimg_save_pandore_case(0,1,0,"char",27); - _cimg_save_pandore_case(0,1,0,"unsigned short",27); - _cimg_save_pandore_case(0,1,0,"short",27); - _cimg_save_pandore_case(0,1,0,"unsigned int",27); - _cimg_save_pandore_case(0,1,0,"int",27); - _cimg_save_pandore_case(0,1,0,"unsigned int64",27); - _cimg_save_pandore_case(0,1,0,"int64",27); - _cimg_save_pandore_case(0,1,0,"float",29); - _cimg_save_pandore_case(0,1,0,"double",29); - - _cimg_save_pandore_case(0,0,0,"unsigned char",30); - _cimg_save_pandore_case(0,0,0,"char",31); - _cimg_save_pandore_case(0,0,0,"unsigned short",31); - _cimg_save_pandore_case(0,0,0,"short",31); - _cimg_save_pandore_case(0,0,0,"unsigned int",31); - _cimg_save_pandore_case(0,0,0,"int",31); - _cimg_save_pandore_case(0,0,0,"unsigned int64",31); - _cimg_save_pandore_case(0,0,0,"int64",31); - _cimg_save_pandore_case(0,0,0,"float",33); - _cimg_save_pandore_case(0,0,0,"double",33); - - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a raw data file. - /** - \param filename Filename, as a C-string. - \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false). - \note The .raw format does not store the image dimensions in the output file, - so you have to keep track of them somewhere to be able to read the file correctly afterwards. - **/ - const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const { - return _save_raw(0,filename,is_multiplexed); - } - - //! Save image as a raw data file \overloading. - /** - Same as save_raw(const char *,bool) const - with a file stream argument instead of a filename string. - **/ - const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const { - return _save_raw(file,0,is_multiplexed); - } - - const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_raw(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - if (!is_multiplexed) cimg::fwrite(_data,size(),nfile); - else { - CImg<T> buf(_spectrum); - cimg_forXYZ(*this,x,y,z) { - cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c); - cimg::fwrite(buf._data,_spectrum,nfile); - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save image as a .yuv video file. - /** - \param filename Filename, as a C-string. - \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>. - \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false). - \note Each slice of the instance image is considered to be a single frame of the output video file. - **/ - const CImg<T>& save_yuv(const char *const filename, - const unsigned int chroma_subsampling=444, - const bool is_rgb=true) const { - CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb); - return *this; - } - - //! Save image as a .yuv video file \overloading. - /** - Same as save_yuv(const char*,const unsigned int,const bool) const - with a file stream argument instead of a filename string. - **/ - const CImg<T>& save_yuv(std::FILE *const file, - const unsigned int chroma_subsampling=444, - const bool is_rgb=true) const { - CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb); - return *this; - } - - //! Save 3D object as an Object File Format (.off) file. - /** - \param filename Filename, as a C-string. - \param primitives List of 3D object primitives. - \param colors List of 3D object colors. - \note - - Instance image contains the vertices data of the 3D object. - - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format. - Such primitives will be lost or simplified during file saving. - - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>. - **/ - template<typename tf, typename tc> - const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors, - const char *const filename) const { - return _save_off(primitives,colors,0,filename); - } - - //! Save 3D object as an Object File Format (.off) file \overloading. - /** - Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const - with a file stream argument instead of a filename string. - **/ - template<typename tf, typename tc> - const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors, - std::FILE *const file) const { - return _save_off(primitives,colors,file,0); - } - - template<typename tf, typename tc> - const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors, - std::FILE *const file, const char *const filename) const { - if (!file && !filename) - throw CImgArgumentException(_cimg_instance - "save_off(): Specified filename is (null).", - cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_off(): Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - - CImgList<T> opacities; - CImg<charT> error_message(1024); - if (!is_object3d(primitives,colors,opacities,true,error_message)) - throw CImgInstanceException(_cimg_instance - "save_off(): Invalid specified 3D object, for file '%s' (%s).", - cimg_instance, - filename?filename:"(FILE*)",error_message.data()); - - const CImg<tc> default_color(1,3,1,1,200); - std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); - unsigned int supported_primitives = 0; - cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives; - std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width); - cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n", - (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); - cimglist_for(primitives,l) { - const CImg<tc>& color = l<colors.width()?colors[l]:default_color; - const unsigned int psiz = primitives[l].size(), csiz = color.size(); - const float r = color[0]/255.f, g = (csiz>1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f; - switch (psiz) { - case 1 : std::fprintf(nfile,"1 %u %f %f %f\n", - (unsigned int)primitives(l,0),r,g,b); break; - case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n", - (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; - case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n", - (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), - (unsigned int)primitives(l,1),r,g,b); break; - case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", - (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), - (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; - case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n", - (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; - case 6 : { - const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3); - const float - rt = color.atXY(xt,yt,0)/255.f, - gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, - bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; - std::fprintf(nfile,"2 %u %u %f %f %f\n", - (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt); - } break; - case 9 : { - const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4); - const float - rt = color.atXY(xt,yt,0)/255.f, - gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, - bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; - std::fprintf(nfile,"3 %u %u %u %f %f %f\n", - (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), - (unsigned int)primitives(l,1),rt,gt,bt); - } break; - case 12 : { - const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5); - const float - rt = color.atXY(xt,yt,0)/255.f, - gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, - bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; - std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", - (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), - (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt); - } break; - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save volumetric image as a video, using the OpenCV library. - /** - \param filename Filename to write data to. - \param fps Number of frames per second. - \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). - \param keep_open Tells if the video writer associated to the specified filename - must be kept open or not (to allow frames to be added in the same file afterwards). - **/ - const CImg<T>& save_video(const char *const filename, const unsigned int fps=25, - const char *codec=0, const bool keep_open=false) const { - if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; } - CImgList<T> list; - get_split('z').move_to(list); - list.save_video(filename,fps,codec,keep_open); - return *this; - } - - //! Save volumetric image as a video, using ffmpeg external binary. - /** - \param filename Filename, as a C-string. - \param fps Video framerate. - \param codec Video codec, as a C-string. - \param bitrate Video bitrate. - \note - - Each slice of the instance image is considered to be a single frame of the output video file. - - This method uses \c ffmpeg, an external executable binary provided by - <a href="http://www.ffmpeg.org">FFmpeg</a>. - It must be installed for the method to succeed. - **/ - const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25, - const char *const codec=0, const unsigned int bitrate=2048) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_ffmpeg_external(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - - CImgList<T> list; - get_split('z').move_to(list); - list.save_ffmpeg_external(filename,fps,codec,bitrate); - return *this; - } - - //! Save image using gzip external binary. - /** - \param filename Filename, as a C-string. - \note This method uses \c gzip, an external executable binary provided by - <a href="//http://www.gzip.org">gzip</a>. - It must be installed for the method to succeed. - **/ - const CImg<T>& save_gzip_external(const char *const filename) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_gzip_external(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - - CImg<charT> command(1024), filename_tmp(256), body(256); - const char - *ext = cimg::split_filename(filename,body), - *ext2 = cimg::split_filename(body,0); - std::FILE *file; - do { - if (!cimg::strcasecmp(ext,"gz")) { - if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - save(filename_tmp); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gzip_path(), - CImg<charT>::string(filename_tmp)._system_strescape().data(), - CImg<charT>::string(filename)._system_strescape().data()); - cimg::system(command); - file = cimg::std_fopen(filename,"rb"); - if (!file) - throw CImgIOException(_cimg_instance - "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", - cimg_instance, - filename); - - else cimg::fclose(file); - std::remove(filename_tmp); - return *this; - } - - //! Save image using GraphicsMagick's external binary. - /** - \param filename Filename, as a C-string. - \param quality Image quality (expressed in percent), when the file format supports it. - \note This method uses \c gm, an external executable binary provided by - <a href="http://www.graphicsmagick.org">GraphicsMagick</a>. - It must be installed for the method to succeed. - **/ - const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_graphicsmagick_external(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_other(): File '%s', saving a volumetric image with an external call to " - "GraphicsMagick only writes the first image slice.", - cimg_instance,filename); - -#ifdef cimg_use_png -#define _cimg_sge_ext1 "png" -#define _cimg_sge_ext2 "png" -#else -#define _cimg_sge_ext1 "pgm" -#define _cimg_sge_ext2 "ppm" -#endif - CImg<charT> command(1024), filename_tmp(256); - std::FILE *file; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(), - _spectrum==1?_cimg_sge_ext1:_cimg_sge_ext2); - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); -#ifdef cimg_use_png - save_png(filename_tmp); -#else - save_pnm(filename_tmp); -#endif - cimg_snprintf(command,command._width,"%s convert -quality %u \"%s\" \"%s\"", - cimg::graphicsmagick_path(),quality, - CImg<charT>::string(filename_tmp)._system_strescape().data(), - CImg<charT>::string(filename)._system_strescape().data()); - cimg::system(command); - file = cimg::std_fopen(filename,"rb"); - if (!file) - throw CImgIOException(_cimg_instance - "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.", - cimg_instance, - filename); - - if (file) cimg::fclose(file); - std::remove(filename_tmp); - return *this; - } - - //! Save image using ImageMagick's external binary. - /** - \param filename Filename, as a C-string. - \param quality Image quality (expressed in percent), when the file format supports it. - \note This method uses \c convert, an external executable binary provided by - <a href="http://www.imagemagick.org">ImageMagick</a>. - It must be installed for the method to succeed. - **/ - const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_imagemagick_external(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_other(): File '%s', saving a volumetric image with an external call to " - "ImageMagick only writes the first image slice.", - cimg_instance,filename); -#ifdef cimg_use_png -#define _cimg_sie_ext1 "png" -#define _cimg_sie_ext2 "png" -#else -#define _cimg_sie_ext1 "pgm" -#define _cimg_sie_ext2 "ppm" -#endif - CImg<charT> command(1024), filename_tmp(256); - std::FILE *file; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(), - cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_ext1:_cimg_sie_ext2); - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); -#ifdef cimg_use_png - save_png(filename_tmp); -#else - save_pnm(filename_tmp); -#endif - cimg_snprintf(command,command._width,"%s -quality %u \"%s\" \"%s\"", - cimg::imagemagick_path(),quality, - CImg<charT>::string(filename_tmp)._system_strescape().data(), - CImg<charT>::string(filename)._system_strescape().data()); - cimg::system(command); - file = cimg::std_fopen(filename,"rb"); - if (!file) - throw CImgIOException(_cimg_instance - "save_imagemagick_external(): Failed to save file '%s' with " - "external command 'magick/convert'.", - cimg_instance, - filename); - - if (file) cimg::fclose(file); - std::remove(filename_tmp); - return *this; - } - - //! Save image as a Dicom file. - /** - \param filename Filename, as a C-string. - \note This method uses \c medcon, an external executable binary provided by - <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>. - It must be installed for the method to succeed. - **/ - const CImg<T>& save_medcon_external(const char *const filename) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_medcon_external(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - - CImg<charT> command(1024), filename_tmp(256), body(256); - std::FILE *file; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - save_analyze(filename_tmp); - cimg_snprintf(command,command._width,"%s -w -c dicom -o \"%s\" -f \"%s\"", - cimg::medcon_path(), - CImg<charT>::string(filename)._system_strescape().data(), - CImg<charT>::string(filename_tmp)._system_strescape().data()); - cimg::system(command); - std::remove(filename_tmp); - cimg::split_filename(filename_tmp,body); - cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data); - std::remove(filename_tmp); - - file = cimg::std_fopen(filename,"rb"); - if (!file) { - cimg_snprintf(command,command._width,"m000-%s",filename); - file = cimg::std_fopen(command,"rb"); - if (!file) { - cimg::fclose(cimg::fopen(filename,"r")); - throw CImgIOException(_cimg_instance - "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.", - cimg_instance, - filename); - } - } - cimg::fclose(file); - std::rename(command,filename); - return *this; - } - - // Save image for non natively supported formats. - /** - \param filename Filename, as a C-string. - \param quality Image quality (expressed in percent), when the file format supports it. - \note - - The filename extension tells about the desired file format. - - This method tries to save the instance image as a file, using external tools from - <a href="http://www.imagemagick.org">ImageMagick</a> or - <a href="http://www.graphicsmagick.org">GraphicsMagick</a>. - At least one of these tool must be installed for the method to succeed. - - It is recommended to use the generic method save(const char*, int) const instead, - as it can handle some file formats natively. - **/ - const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_other(): Specified filename is (null).", - cimg_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - if (_depth>1) - cimg::warn(_cimg_instance - "save_other(): File '%s', saving a volumetric image with an external call to " - "ImageMagick or GraphicsMagick only writes the first image slice.", - cimg_instance,filename); - - const unsigned int omode = cimg::exception_mode(); - bool is_saved = true; - cimg::exception_mode(0); - try { save_magick(filename); } - catch (CImgException&) { - try { save_imagemagick_external(filename,quality); } - catch (CImgException&) { - try { save_graphicsmagick_external(filename,quality); } - catch (CImgException&) { - is_saved = false; - } - } - } - cimg::exception_mode(omode); - if (!is_saved) - throw CImgIOException(_cimg_instance - "save_other(): Failed to save file '%s'. Format is not natively supported, " - "and no external commands succeeded.", - cimg_instance, - filename); - return *this; - } - - //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer. - /** - \param is_compressed tells if zlib compression must be used for serialization - (this requires 'cimg_use_zlib' been enabled). - **/ - CImg<ucharT> get_serialize(const bool is_compressed=false) const { - return CImgList<T>(*this,true).get_serialize(is_compressed); - } - - // [internal] Return a 40x38 color logo of a 'danger' item. - static CImg<T> _logo40x38() { - CImg<T> res(40,38,1,3); - const unsigned char *ptrs = cimg::logo40x38; - T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2); - for (ulongT off = 0; off<(ulongT)res._width*res._height;) { - const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++); - for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; } - } - return res; - } - - //@} - }; - - /* - #----------------------------------------- - # - # - # - # Definition of the CImgList<T> structure - # - # - # - #------------------------------------------ - */ - //! Represent a list of images CImg<T>. - template<typename T> - struct CImgList { - unsigned int _width, _allocated_width; - CImg<T> *_data; - - //! Simple iterator type, to loop through each image of a list. - /** - \note - - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>. - - You may use it like this: - \code - CImgList<> list; // Assuming this image list is not empty - for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x'); - \endcode - - Using the loop macro \c cimglist_for is another (more concise) alternative: - \code - cimglist_for(list,l) list[l].mirror('x'); - \endcode - **/ - typedef CImg<T>* iterator; - - //! Simple const iterator type, to loop through each image of a \c const list instance. - /** - \note - - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>. - - Similar to CImgList<T>::iterator, but for constant list instances. - **/ - typedef const CImg<T>* const_iterator; - - //! Pixel value type. - /** - Refer to the pixels value type of the images in the list. - \note - - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T. - It is then similar to CImg<T>::value_type. - - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for - compatibility with STL naming conventions. - **/ - typedef T value_type; - - // Define common types related to template type T. - typedef typename cimg::superset<T,bool>::type Tbool; - typedef typename cimg::superset<T,unsigned char>::type Tuchar; - typedef typename cimg::superset<T,char>::type Tchar; - typedef typename cimg::superset<T,unsigned short>::type Tushort; - typedef typename cimg::superset<T,short>::type Tshort; - typedef typename cimg::superset<T,unsigned int>::type Tuint; - typedef typename cimg::superset<T,int>::type Tint; - typedef typename cimg::superset<T,cimg_ulong>::type Tulong; - typedef typename cimg::superset<T,cimg_long>::type Tlong; - typedef typename cimg::superset<T,float>::type Tfloat; - typedef typename cimg::superset<T,double>::type Tdouble; - typedef typename cimg::last<T,bool>::type boolT; - typedef typename cimg::last<T,unsigned char>::type ucharT; - typedef typename cimg::last<T,char>::type charT; - typedef typename cimg::last<T,unsigned short>::type ushortT; - typedef typename cimg::last<T,short>::type shortT; - typedef typename cimg::last<T,unsigned int>::type uintT; - typedef typename cimg::last<T,int>::type intT; - typedef typename cimg::last<T,cimg_ulong>::type ulongT; - typedef typename cimg::last<T,cimg_long>::type longT; - typedef typename cimg::last<T,cimg_uint64>::type uint64T; - typedef typename cimg::last<T,cimg_int64>::type int64T; - typedef typename cimg::last<T,float>::type floatT; - typedef typename cimg::last<T,double>::type doubleT; - - //@} - //--------------------------- - // - //! \name Plugins - //@{ - //--------------------------- -#ifdef cimglist_plugin -#include cimglist_plugin -#endif -#ifdef cimglist_plugin1 -#include cimglist_plugin1 -#endif -#ifdef cimglist_plugin2 -#include cimglist_plugin2 -#endif -#ifdef cimglist_plugin3 -#include cimglist_plugin3 -#endif -#ifdef cimglist_plugin4 -#include cimglist_plugin4 -#endif -#ifdef cimglist_plugin5 -#include cimglist_plugin5 -#endif -#ifdef cimglist_plugin6 -#include cimglist_plugin6 -#endif -#ifdef cimglist_plugin7 -#include cimglist_plugin7 -#endif -#ifdef cimglist_plugin8 -#include cimglist_plugin8 -#endif - - //@} - //-------------------------------------------------------- - // - //! \name Constructors / Destructor / Instance Management - //@{ - //-------------------------------------------------------- - - //! Destructor. - /** - Destroy current list instance. - \note - - Any allocated buffer is deallocated. - - Destroying an empty list does nothing actually. - **/ - ~CImgList() { - delete[] _data; - } - - //! Default constructor. - /** - Construct a new empty list instance. - \note - - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its - image buffer pointer data(). - - An empty list may be reassigned afterwards, with the family of the assign() methods. - In all cases, the type of pixels stays \c T. - **/ - CImgList(): - _width(0),_allocated_width(0),_data(0) {} - - //! Construct list containing empty images. - /** - \param n Number of empty images. - \note Useful when you know by advance the number of images you want to manage, as - it will allocate the right amount of memory for the list, without needs for reallocation - (that may occur when starting from an empty list and inserting several images in it). - **/ - explicit CImgList(const unsigned int n):_width(n) { - if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; - else { _allocated_width = 0; _data = 0; } - } - - //! Construct list containing images of specified size. - /** - \param n Number of images. - \param width Width of images. - \param height Height of images. - \param depth Depth of images. - \param spectrum Number of channels of images. - \note Pixel values are not initialized and may probably contain garbage. - **/ - CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, - const unsigned int depth=1, const unsigned int spectrum=1): - _width(0),_allocated_width(0),_data(0) { - assign(n); - cimglist_apply(*this,assign)(width,height,depth,spectrum); - } - - //! Construct list containing images of specified size, and initialize pixel values. - /** - \param n Number of images. - \param width Width of images. - \param height Height of images. - \param depth Depth of images. - \param spectrum Number of channels of images. - \param val Initialization value for images pixels. - **/ - CImgList(const unsigned int n, const unsigned int width, const unsigned int height, - const unsigned int depth, const unsigned int spectrum, const T& val): - _width(0),_allocated_width(0),_data(0) { - assign(n); - cimglist_apply(*this,assign)(width,height,depth,spectrum,val); - } - - //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers. - /** - \param n Number of images. - \param width Width of images. - \param height Height of images. - \param depth Depth of images. - \param spectrum Number of channels of images. - \param val0 First value of the initializing integers sequence. - \param val1 Second value of the initializing integers sequence. - \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list, - or you will probably segfault. - **/ - CImgList(const unsigned int n, const unsigned int width, const unsigned int height, - const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...): - _width(0),_allocated_width(0),_data(0) { -#define _CImgList_stdarg(t) { \ - assign(n,width,height,depth,spectrum); \ - const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \ - T *ptrd = _data->_data; \ - va_list ap; \ - va_start(ap,val1); \ - for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \ - *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \ - if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \ - } \ - va_end(ap); \ - } - _CImgList_stdarg(int); - } - - //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles. - /** - \param n Number of images. - \param width Width of images. - \param height Height of images. - \param depth Depth of images. - \param spectrum Number of channels of images. - \param val0 First value of the initializing doubles sequence. - \param val1 Second value of the initializing doubles sequence. - \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list, - or you will probably segfault. - **/ - CImgList(const unsigned int n, const unsigned int width, const unsigned int height, - const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...): - _width(0),_allocated_width(0),_data(0) { - _CImgList_stdarg(double); - } - - //! Construct list containing copies of an input image. - /** - \param n Number of images. - \param img Input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img. - **/ - template<typename t> - CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(n); - cimglist_apply(*this,assign)(img,is_shared); - } - - //! Construct list from one image. - /** - \param img Input image to copy in the constructed list. - \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img. - **/ - template<typename t> - explicit CImgList(const CImg<t>& img, const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(1); - _data[0].assign(img,is_shared); - } - - //! Construct list from two images. - /** - \param img1 First input image to copy in the constructed list. - \param img2 Second input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t1, typename t2> - CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(2); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); - } - - //! Construct list from three images. - /** - \param img1 First input image to copy in the constructed list. - \param img2 Second input image to copy in the constructed list. - \param img3 Third input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t1, typename t2, typename t3> - CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(3); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - } - - //! Construct list from four images. - /** - \param img1 First input image to copy in the constructed list. - \param img2 Second input image to copy in the constructed list. - \param img3 Third input image to copy in the constructed list. - \param img4 Fourth input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t1, typename t2, typename t3, typename t4> - CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(4); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); - } - - //! Construct list from five images. - /** - \param img1 First input image to copy in the constructed list. - \param img2 Second input image to copy in the constructed list. - \param img3 Third input image to copy in the constructed list. - \param img4 Fourth input image to copy in the constructed list. - \param img5 Fifth input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5> - CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(5); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); - } - - //! Construct list from six images. - /** - \param img1 First input image to copy in the constructed list. - \param img2 Second input image to copy in the constructed list. - \param img3 Third input image to copy in the constructed list. - \param img4 Fourth input image to copy in the constructed list. - \param img5 Fifth input image to copy in the constructed list. - \param img6 Sixth input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6> - CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(6); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); - } - - //! Construct list from seven images. - /** - \param img1 First input image to copy in the constructed list. - \param img2 Second input image to copy in the constructed list. - \param img3 Third input image to copy in the constructed list. - \param img4 Fourth input image to copy in the constructed list. - \param img5 Fifth input image to copy in the constructed list. - \param img6 Sixth input image to copy in the constructed list. - \param img7 Seventh input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7> - CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(7); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); - _data[6].assign(img7,is_shared); - } - - //! Construct list from eight images. - /** - \param img1 First input image to copy in the constructed list. - \param img2 Second input image to copy in the constructed list. - \param img3 Third input image to copy in the constructed list. - \param img4 Fourth input image to copy in the constructed list. - \param img5 Fifth input image to copy in the constructed list. - \param img6 Sixth input image to copy in the constructed list. - \param img7 Seventh input image to copy in the constructed list. - \param img8 Eighth input image to copy in the constructed list. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8> - CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, - const bool is_shared=false): - _width(0),_allocated_width(0),_data(0) { - assign(8); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); - _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); - } - - //! Construct list copy. - /** - \param list Input list to copy. - \note The shared state of each element of the constructed list is kept the same as in \c list. - **/ - template<typename t> - CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) { - assign(list._width); - cimglist_for(*this,l) _data[l].assign(list[l],false); - } - - //! Construct list copy \specialization. - CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) { - assign(list._width); - cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared); - } - - //! Construct list copy, and force the shared state of the list elements. - /** - \param list Input list to copy. - \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. - **/ - template<typename t> - CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) { - assign(list._width); - cimglist_for(*this,l) _data[l].assign(list[l],is_shared); - } - - //! Construct list by reading the content of a file. - /** - \param filename Filename, as a C-string. - **/ - explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) { - assign(filename); - } - - //! Construct list from the content of a display window. - /** - \param disp Display window to get content from. - \note Constructed list contains a single image only. - **/ - explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) { - assign(disp); - } - - //! Return a list with elements being shared copies of images in the list instance. - /** - \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>. - **/ - CImgList<T> get_shared() { - CImgList<T> res(_width); - cimglist_for(*this,l) res[l].assign(_data[l],true); - return res; - } - - //! Return a list with elements being shared copies of images in the list instance \const. - const CImgList<T> get_shared() const { - CImgList<T> res(_width); - cimglist_for(*this,l) res[l].assign(_data[l],true); - return res; - } - - //! Destructor \inplace. - /** - \see CImgList(). - **/ - CImgList<T>& assign() { - delete[] _data; - _width = _allocated_width = 0; - _data = 0; - return *this; - } - - //! Destructor \inplace. - /** - Equivalent to assign(). - \note Only here for compatibility with STL naming conventions. - **/ - CImgList<T>& clear() { - return assign(); - } - - //! Construct list containing empty images \inplace. - /** - \see CImgList(unsigned int). - **/ - CImgList<T>& assign(const unsigned int n) { - if (!n) return assign(); - if (_allocated_width<n || _allocated_width>(n<<2)) { - delete[] _data; - _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; - } - _width = n; - return *this; - } - - //! Construct list containing images of specified size \inplace. - /** - \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int). - **/ - CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, - const unsigned int depth=1, const unsigned int spectrum=1) { - assign(n); - cimglist_apply(*this,assign)(width,height,depth,spectrum); - return *this; - } - - //! Construct list containing images of specified size, and initialize pixel values \inplace. - /** - \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T). - **/ - CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height, - const unsigned int depth, const unsigned int spectrum, const T& val) { - assign(n); - cimglist_apply(*this,assign)(width,height,depth,spectrum,val); - return *this; - } - - //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace. - /** - \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...). - **/ - CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height, - const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) { - _CImgList_stdarg(int); - return *this; - } - - //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace. - /** - \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...). - **/ - CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height, - const unsigned int depth, const unsigned int spectrum, - const double val0, const double val1, ...) { - _CImgList_stdarg(double); - return *this; - } - - //! Construct list containing copies of an input image \inplace. - /** - \see CImgList(unsigned int, const CImg<t>&, bool). - **/ - template<typename t> - CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) { - assign(n); - cimglist_apply(*this,assign)(img,is_shared); - return *this; - } - - //! Construct list from one image \inplace. - /** - \see CImgList(const CImg<t>&, bool). - **/ - template<typename t> - CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) { - assign(1); - _data[0].assign(img,is_shared); - return *this; - } - - //! Construct list from two images \inplace. - /** - \see CImgList(const CImg<t>&, const CImg<t>&, bool). - **/ - template<typename t1, typename t2> - CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) { - assign(2); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); - return *this; - } - - //! Construct list from three images \inplace. - /** - \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool). - **/ - template<typename t1, typename t2, typename t3> - CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) { - assign(3); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - return *this; - } - - //! Construct list from four images \inplace. - /** - \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool). - **/ - template<typename t1, typename t2, typename t3, typename t4> - CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const bool is_shared=false) { - assign(4); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); - return *this; - } - - //! Construct list from five images \inplace. - /** - \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool). - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5> - CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const bool is_shared=false) { - assign(5); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); - return *this; - } - - //! Construct list from six images \inplace. - /** - \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool). - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6> - CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) { - assign(6); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); - return *this; - } - - //! Construct list from seven images \inplace. - /** - \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, - const CImg<t>&, bool). - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7> - CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) { - assign(7); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); - _data[6].assign(img7,is_shared); - return *this; - } - - //! Construct list from eight images \inplace. - /** - \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, - const CImg<t>&, const CImg<t>&, bool). - **/ - template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8> - CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, - const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, - const bool is_shared=false) { - assign(8); - _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); - _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); - _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); - return *this; - } - - //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace. - /** - \see CImgList(const CImgList<t>&, bool is_shared). - **/ - template<typename t> - CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) { - cimg::unused(is_shared); - assign(list._width); - cimglist_for(*this,l) _data[l].assign(list[l],false); - return *this; - } - - //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization. - CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) { - if (this==&list) return *this; - CImgList<T> res(list._width); - cimglist_for(res,l) res[l].assign(list[l],is_shared); - return res.move_to(*this); - } - - //! Construct list by reading the content of a file \inplace. - /** - \see CImgList(const char *const). - **/ - CImgList<T>& assign(const char *const filename) { - return load(filename); - } - - //! Construct list from the content of a display window \inplace. - /** - \see CImgList(const CImgDisplay&). - **/ - CImgList<T>& assign(const CImgDisplay &disp) { - return assign(CImg<T>(disp)); - } - - //! Transfer the content of the list instance to another list. - /** - \param list Destination list. - \note When returning, the current list instance is empty and the initial content of \c list is destroyed. - **/ - template<typename t> - CImgList<t>& move_to(CImgList<t>& list) { - list.assign(_width); - bool is_one_shared_element = false; - cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; - if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]); - else cimglist_for(*this,l) _data[l].move_to(list[l]); - assign(); - return list; - } - - //! Transfer the content of the list instance at a specified position in another list. - /** - \param list Destination list. - \param pos Index of the insertion in the list. - \note When returning, the list instance is empty and the initial content of \c list is preserved - (only images indexes may be modified). - **/ - template<typename t> - CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) { - if (is_empty()) return list; - const unsigned int npos = pos>list._width?list._width:pos; - list.insert(_width,npos); - bool is_one_shared_element = false; - cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; - if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]); - else cimglist_for(*this,l) _data[l].move_to(list[npos + l]); - assign(); - return list; - } - - //! Swap all fields between two list instances. - /** - \param list List to swap fields with. - \note Can be used to exchange the content of two lists in a fast way. - **/ - CImgList<T>& swap(CImgList<T>& list) { - cimg::swap(_width,list._width,_allocated_width,list._allocated_width); - cimg::swap(_data,list._data); - return list; - } - - //! Return a reference to an empty list. - /** - \note Can be used to define default values in a function taking a CImgList<T> as an argument. - \code - void f(const CImgList<char>& list=CImgList<char>::empty()); - \endcode - **/ - static CImgList<T>& empty() { - static CImgList<T> _empty; - return _empty.assign(); - } - - //! Return a reference to an empty list \const. - static const CImgList<T>& const_empty() { - static const CImgList<T> _empty; - return _empty; - } - - //@} - //------------------------------------------ - // - //! \name Overloaded Operators - //@{ - //------------------------------------------ - - //! Return a reference to one image element of the list. - /** - \param pos Indice of the image element. - **/ - CImg<T>& operator()(const unsigned int pos) { -#if cimg_verbosity>=3 - if (pos>=_width) { - cimg::warn(_cimglist_instance - "operator(): Invalid image request, at position [%u].", - cimglist_instance, - pos); - return *_data; - } -#endif - return _data[pos]; - } - - //! Return a reference to one image of the list. - /** - \param pos Indice of the image element. - **/ - const CImg<T>& operator()(const unsigned int pos) const { - return const_cast<CImgList<T>*>(this)->operator()(pos); - } - - //! Return a reference to one pixel value of one image of the list. - /** - \param pos Indice of the image element. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>. - **/ - T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, - const unsigned int z=0, const unsigned int c=0) { - return (*this)[pos](x,y,z,c); - } - - //! Return a reference to one pixel value of one image of the list \const. - const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, - const unsigned int z=0, const unsigned int c=0) const { - return (*this)[pos](x,y,z,c); - } - - //! Return pointer to the first image of the list. - /** - \note Images in a list are stored as a buffer of \c CImg<T>. - **/ - operator CImg<T>*() { - return _data; - } - - //! Return pointer to the first image of the list \const. - operator const CImg<T>*() const { - return _data; - } - - //! Construct list from one image \inplace. - /** - \param img Input image to copy in the constructed list. - \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>. - **/ - template<typename t> - CImgList<T>& operator=(const CImg<t>& img) { - return assign(img); - } - - //! Construct list from another list. - /** - \param list Input list to copy. - \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>. - **/ - template<typename t> - CImgList<T>& operator=(const CImgList<t>& list) { - return assign(list); - } - - //! Construct list from another list \specialization. - CImgList<T>& operator=(const CImgList<T>& list) { - return assign(list); - } - - //! Construct list by reading the content of a file \inplace. - /** - \see CImgList(const char *const). - **/ - CImgList<T>& operator=(const char *const filename) { - return assign(filename); - } - - //! Construct list from the content of a display window \inplace. - /** - \see CImgList(const CImgDisplay&). - **/ - CImgList<T>& operator=(const CImgDisplay& disp) { - return assign(disp); - } - - //! Return a non-shared copy of a list. - /** - \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>. - It forces the copy to have non-shared elements. - **/ - CImgList<T> operator+() const { - return CImgList<T>(*this,false); - } - - //! Return a copy of the list instance, where image \c img has been inserted at the end. - /** - \param img Image inserted at the end of the instance copy. - \note Define a convenient way to create temporary lists of images, as in the following code: - \code - (img1,img2,img3,img4).display("My four images"); - \endcode - **/ - template<typename t> - CImgList<T>& operator,(const CImg<t>& img) { - return insert(img); - } - - //! Return a copy of the list instance, where image \c img has been inserted at the end \const. - template<typename t> - CImgList<T> operator,(const CImg<t>& img) const { - return (+*this).insert(img); - } - - //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end. - /** - \param list List inserted at the end of the instance copy. - **/ - template<typename t> - CImgList<T>& operator,(const CImgList<t>& list) { - return insert(list); - } - - //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const. - template<typename t> - CImgList<T>& operator,(const CImgList<t>& list) const { - return (+*this).insert(list); - } - - //! Return image corresponding to the appending of all images of the instance list along specified axis. - /** - \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>. - **/ - CImg<T> operator>(const char axis) const { - return get_append(axis,0); - } - - //! Return list corresponding to the splitting of all images of the instance list along specified axis. - /** - \param axis Axis used for image splitting. - \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>. - **/ - CImgList<T> operator<(const char axis) const { - return get_split(axis); - } - - //@} - //------------------------------------- - // - //! \name Instance Characteristics - //@{ - //------------------------------------- - - //! Return the type of image pixel values as a C string. - /** - Return a \c char* string containing the usual type name of the image pixel values - (i.e. a stringified version of the template parameter \c T). - \note - - The returned string may contain spaces (as in \c "unsigned char"). - - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned. - **/ - static const char* pixel_type() { - return cimg::type<T>::string(); - } - - //! Return the size of the list, i.e. the number of images contained in it. - /** - \note Similar to size() but returns result as a (signed) integer. - **/ - int width() const { - return (int)_width; - } - - //! Return the size of the list, i.e. the number of images contained in it. - /** - \note Similar to width() but returns result as an unsigned integer. - **/ - unsigned int size() const { - return _width; - } - - //! Return pointer to the first image of the list. - /** - \note Images in a list are stored as a buffer of \c CImg<T>. - **/ - CImg<T> *data() { - return _data; - } - - //! Return pointer to the first image of the list \const. - const CImg<T> *data() const { - return _data; - } - - //! Return pointer to the pos-th image of the list. - /** - \param pos Indice of the image element to access. - \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>. - **/ -#if cimg_verbosity>=3 - CImg<T> *data(const unsigned int pos) { - if (pos>=size()) - cimg::warn(_cimglist_instance - "data(): Invalid pointer request, at position [%u].", - cimglist_instance, - pos); - return _data + pos; - } - - const CImg<T> *data(const unsigned int l) const { - return const_cast<CImgList<T>*>(this)->data(l); - } -#else - CImg<T> *data(const unsigned int l) { - return _data + l; - } - - //! Return pointer to the pos-th image of the list \const. - const CImg<T> *data(const unsigned int l) const { - return _data + l; - } -#endif - - //! Return iterator to the first image of the list. - /** - **/ - iterator begin() { - return _data; - } - - //! Return iterator to the first image of the list \const. - const_iterator begin() const { - return _data; - } - - //! Return iterator to one position after the last image of the list. - /** - **/ - iterator end() { - return _data + _width; - } - - //! Return iterator to one position after the last image of the list \const. - const_iterator end() const { - return _data + _width; - } - - //! Return reference to the first image of the list. - /** - **/ - CImg<T>& front() { - return *_data; - } - - //! Return reference to the first image of the list \const. - const CImg<T>& front() const { - return *_data; - } - - //! Return a reference to the last image of the list. - /** - **/ - const CImg<T>& back() const { - return *(_data + _width - 1); - } - - //! Return a reference to the last image of the list \const. - CImg<T>& back() { - return *(_data + _width - 1); - } - - //! Return pos-th image of the list. - /** - \param pos Indice of the image element to access. - **/ - CImg<T>& at(const int pos) { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "at(): Empty instance.", - cimglist_instance); - - return _data[cimg::cut(pos,0,width() - 1)]; - } - - //! Access to pixel value with Dirichlet boundary conditions. - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c offset is outside image bounds. - \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>. - **/ - T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { - return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value); - } - - //! Access to pixel value with Dirichlet boundary conditions \const. - T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { - return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value); - } - - //! Access to pixel value with Neumann boundary conditions. - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>. - **/ - T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNXYZC(): Empty instance.", - cimglist_instance); - - return _atNXYZC(pos,x,y,z,c); - } - - //! Access to pixel value with Neumann boundary conditions \const. - T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNXYZC(): Empty instance.", - cimglist_instance); - - return _atNXYZC(pos,x,y,z,c); - } - - T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) { - return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); - } - - T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { - return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); - } - - //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c offset is outside image bounds. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { - return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value); - } - - //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const. - T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { - return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value); - } - - //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNXYZ(): Empty instance.", - cimglist_instance); - - return _atNXYZ(pos,x,y,z,c); - } - - //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const. - T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNXYZ(): Empty instance.", - cimglist_instance); - - return _atNXYZ(pos,x,y,z,c); - } - - T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { - return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); - } - - T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { - return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); - } - - //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c offset is outside image bounds. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { - return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value); - } - - //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. - T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { - return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value); - } - - //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNXY(): Empty instance.", - cimglist_instance); - - return _atNXY(pos,x,y,z,c); - } - - //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. - T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNXY(): Empty instance.", - cimglist_instance); - - return _atNXY(pos,x,y,z,c); - } - - T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { - return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); - } - - T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { - return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); - } - - //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c offset is outside image bounds. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { - return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value); - } - - //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const. - T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { - return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value); - } - - //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNX(): Empty instance.", - cimglist_instance); - - return _atNX(pos,x,y,z,c); - } - - //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const. - T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atNX(): Empty instance.", - cimglist_instance); - - return _atNX(pos,x,y,z,c); - } - - T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { - return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); - } - - T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { - return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); - } - - //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \param out_value Default value returned if \c offset is outside image bounds. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { - return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c); - } - - //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const. - T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { - return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c); - } - - //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos). - /** - \param pos Indice of the image element to access. - \param x X-coordinate of the pixel value. - \param y Y-coordinate of the pixel value. - \param z Z-coordinate of the pixel value. - \param c C-coordinate of the pixel value. - \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>. - **/ - T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atN(): Empty instance.", - cimglist_instance); - return _atN(pos,x,y,z,c); - } - - //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const. - T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "atN(): Empty instance.", - cimglist_instance); - return _atN(pos,x,y,z,c); - } - - T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { - return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); - } - - T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { - return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); - } - - //@} - //------------------------------------- - // - //! \name Instance Checking - //@{ - //------------------------------------- - - //! Return \c true if list is empty. - /** - **/ - bool is_empty() const { - return (!_data || !_width); - } - - //! Test if number of image elements is equal to specified value. - /** - \param size_n Number of image elements to test. - **/ - bool is_sameN(const unsigned int size_n) const { - return _width==size_n; - } - - //! Test if number of image elements is equal between two images lists. - /** - \param list Input list to compare with. - **/ - template<typename t> - bool is_sameN(const CImgList<t>& list) const { - return is_sameN(list._width); - } - - // Define useful functions to check list dimensions. - // (cannot be documented because macro-generated). -#define _cimglist_def_is_same1(axis) \ - bool is_same##axis(const unsigned int val) const { \ - bool res = true; \ - for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \ - } \ - bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \ - return is_sameN(n) && is_same##axis(val); \ - } \ - -#define _cimglist_def_is_same2(axis1,axis2) \ - bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \ - bool res = true; \ - for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \ - } \ - bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \ - return is_sameN(n) && is_same##axis1##axis2(val1,val2); \ - } \ - -#define _cimglist_def_is_same3(axis1,axis2,axis3) \ - bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \ - const unsigned int val3) const { \ - bool res = true; \ - for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \ - return res; \ - } \ - bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \ - const unsigned int val2, const unsigned int val3) const { \ - return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \ - } \ - -#define _cimglist_def_is_same(axis) \ - template<typename t> bool is_same##axis(const CImg<t>& img) const { \ - bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \ - } \ - template<typename t> bool is_same##axis(const CImgList<t>& list) const { \ - const unsigned int lmin = std::min(_width,list._width); \ - bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \ - } \ - template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \ - return (is_sameN(n) && is_same##axis(img)); \ - } \ - template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \ - return (is_sameN(list) && is_same##axis(list)); \ - } - - _cimglist_def_is_same(XY) - _cimglist_def_is_same(XZ) - _cimglist_def_is_same(XC) - _cimglist_def_is_same(YZ) - _cimglist_def_is_same(YC) - _cimglist_def_is_same(XYZ) - _cimglist_def_is_same(XYC) - _cimglist_def_is_same(YZC) - _cimglist_def_is_same(XYZC) - _cimglist_def_is_same1(X) - _cimglist_def_is_same1(Y) - _cimglist_def_is_same1(Z) - _cimglist_def_is_same1(C) - _cimglist_def_is_same2(X,Y) - _cimglist_def_is_same2(X,Z) - _cimglist_def_is_same2(X,C) - _cimglist_def_is_same2(Y,Z) - _cimglist_def_is_same2(Y,C) - _cimglist_def_is_same2(Z,C) - _cimglist_def_is_same3(X,Y,Z) - _cimglist_def_is_same3(X,Y,C) - _cimglist_def_is_same3(X,Z,C) - _cimglist_def_is_same3(Y,Z,C) - - //! Test if dimensions of each image of the list match specified arguments. - /** - \param dx Checked image width. - \param dy Checked image height. - \param dz Checked image depth. - \param dc Checked image spectrum. - **/ - bool is_sameXYZC(const unsigned int dx, const unsigned int dy, - const unsigned int dz, const unsigned int dc) const { - bool res = true; - for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc); - return res; - } - - //! Test if list dimensions match specified arguments. - /** - \param n Number of images in the list. - \param dx Checked image width. - \param dy Checked image height. - \param dz Checked image depth. - \param dc Checked image spectrum. - **/ - bool is_sameNXYZC(const unsigned int n, - const unsigned int dx, const unsigned int dy, - const unsigned int dz, const unsigned int dc) const { - return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc); - } - - //! Test if list contains one particular pixel location. - /** - \param n Index of the image whom checked pixel value belong to. - \param x X-coordinate of the checked pixel value. - \param y Y-coordinate of the checked pixel value. - \param z Z-coordinate of the checked pixel value. - \param c C-coordinate of the checked pixel value. - **/ - bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const { - if (is_empty()) return false; - return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() && - z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum(); - } - - //! Test if list contains image with specified indice. - /** - \param n Index of the checked image. - **/ - bool containsN(const int n) const { - if (is_empty()) return false; - return n>=0 && n<(int)_width; - } - - //! Test if one image of the list contains the specified referenced value. - /** - \param pixel Reference to pixel value to test. - \param[out] n Index of image containing the pixel value, if test succeeds. - \param[out] x X-coordinate of the pixel value, if test succeeds. - \param[out] y Y-coordinate of the pixel value, if test succeeds. - \param[out] z Z-coordinate of the pixel value, if test succeeds. - \param[out] c C-coordinate of the pixel value, if test succeeds. - \note If true, set coordinates (n,x,y,z,c). - **/ - template<typename t> - bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const { - if (is_empty()) return false; - cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; } - return false; - } - - //! Test if one of the image list contains the specified referenced value. - /** - \param pixel Reference to pixel value to test. - \param[out] n Index of image containing the pixel value, if test succeeds. - \param[out] x X-coordinate of the pixel value, if test succeeds. - \param[out] y Y-coordinate of the pixel value, if test succeeds. - \param[out] z Z-coordinate of the pixel value, if test succeeds. - \note If true, set coordinates (n,x,y,z). - **/ - template<typename t> - bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { - t c; - return contains(pixel,n,x,y,z,c); - } - - //! Test if one of the image list contains the specified referenced value. - /** - \param pixel Reference to pixel value to test. - \param[out] n Index of image containing the pixel value, if test succeeds. - \param[out] x X-coordinate of the pixel value, if test succeeds. - \param[out] y Y-coordinate of the pixel value, if test succeeds. - \note If true, set coordinates (n,x,y). - **/ - template<typename t> - bool contains(const T& pixel, t& n, t& x, t&y) const { - t z, c; - return contains(pixel,n,x,y,z,c); - } - - //! Test if one of the image list contains the specified referenced value. - /** - \param pixel Reference to pixel value to test. - \param[out] n Index of image containing the pixel value, if test succeeds. - \param[out] x X-coordinate of the pixel value, if test succeeds. - \note If true, set coordinates (n,x). - **/ - template<typename t> - bool contains(const T& pixel, t& n, t& x) const { - t y, z, c; - return contains(pixel,n,x,y,z,c); - } - - //! Test if one of the image list contains the specified referenced value. - /** - \param pixel Reference to pixel value to test. - \param[out] n Index of image containing the pixel value, if test succeeds. - \note If true, set coordinates (n). - **/ - template<typename t> - bool contains(const T& pixel, t& n) const { - t x, y, z, c; - return contains(pixel,n,x,y,z,c); - } - - //! Test if one of the image list contains the specified referenced value. - /** - \param pixel Reference to pixel value to test. - **/ - bool contains(const T& pixel) const { - unsigned int n, x, y, z, c; - return contains(pixel,n,x,y,z,c); - } - - //! Test if the list contains the image 'img'. - /** - \param img Reference to image to test. - \param[out] n Index of image in the list, if test succeeds. - \note If true, returns the position (n) of the image in the list. - **/ - template<typename t> - bool contains(const CImg<T>& img, t& n) const { - if (is_empty()) return false; - const CImg<T> *const ptr = &img; - cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; } - return false; - } - - //! Test if the list contains the image img. - /** - \param img Reference to image to test. - **/ - bool contains(const CImg<T>& img) const { - unsigned int n; - return contains(img,n); - } - - //@} - //------------------------------------- - // - //! \name Mathematical Functions - //@{ - //------------------------------------- - - //! Return a reference to the minimum pixel value of the instance list. - /** - **/ - T& min() { - bool is_all_empty = true; - T *ptr_min = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_min = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "min(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T min_value = *ptr_min; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs); - } - return *ptr_min; - } - - //! Return a reference to the minimum pixel value of the instance list \const. - const T& min() const { - bool is_all_empty = true; - T *ptr_min = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_min = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "min(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T min_value = *ptr_min; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs); - } - return *ptr_min; - } - - //! Return a reference to the maximum pixel value of the instance list. - /** - **/ - T& max() { - bool is_all_empty = true; - T *ptr_max = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_max = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "max(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T max_value = *ptr_max; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); - } - return *ptr_max; - } - - //! Return a reference to the maximum pixel value of the instance list \const. - const T& max() const { - bool is_all_empty = true; - T *ptr_max = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_max = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "max(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T max_value = *ptr_max; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); - } - return *ptr_max; - } - - //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well. - /** - \param[out] max_val Value of the maximum value found. - **/ - template<typename t> - T& min_max(t& max_val) { - bool is_all_empty = true; - T *ptr_min = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_min = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "min_max(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T min_value = *ptr_min, max_value = min_value; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) { - const T val = *ptrs; - if (val<min_value) { min_value = val; ptr_min = ptrs; } - if (val>max_value) max_value = val; - } - } - max_val = (t)max_value; - return *ptr_min; - } - - //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const. - /** - \param[out] max_val Value of the maximum value found. - **/ - template<typename t> - const T& min_max(t& max_val) const { - bool is_all_empty = true; - T *ptr_min = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_min = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "min_max(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T min_value = *ptr_min, max_value = min_value; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) { - const T val = *ptrs; - if (val<min_value) { min_value = val; ptr_min = ptrs; } - if (val>max_value) max_value = val; - } - } - max_val = (t)max_value; - return *ptr_min; - } - - //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well. - /** - \param[out] min_val Value of the minimum value found. - **/ - template<typename t> - T& max_min(t& min_val) { - bool is_all_empty = true; - T *ptr_max = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_max = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "max_min(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T min_value = *ptr_max, max_value = min_value; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) { - const T val = *ptrs; - if (val>max_value) { max_value = val; ptr_max = ptrs; } - if (val<min_value) min_value = val; - } - } - min_val = (t)min_value; - return *ptr_max; - } - - //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const. - template<typename t> - const T& max_min(t& min_val) const { - bool is_all_empty = true; - T *ptr_max = 0; - cimglist_for(*this,l) if (!_data[l].is_empty()) { - ptr_max = _data[l]._data; - is_all_empty = false; - break; - } - if (is_all_empty) - throw CImgInstanceException(_cimglist_instance - "max_min(): %s.", - _data?"List of empty images":"Empty instance", - cimglist_instance); - T min_value = *ptr_max, max_value = min_value; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_for(img,ptrs,T) { - const T val = *ptrs; - if (val>max_value) { max_value = val; ptr_max = ptrs; } - if (val<min_value) min_value = val; - } - } - min_val = (t)min_value; - return *ptr_max; - } - - //@} - //--------------------------- - // - //! \name List Manipulation - //@{ - //--------------------------- - - //! Insert a copy of the image \c img into the current image list, at position \c pos. - /** - \param img Image to insert a copy to the list. - \param pos Index of the insertion. - \param is_shared Tells if the inserted image is a shared copy of \c img or not. - **/ - template<typename t> - CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) { - const unsigned int npos = pos==~0U?_width:pos; - if (npos>_width) - throw CImgArgumentException(_cimglist_instance - "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " - "at position %u.", - cimglist_instance, - img._width,img._height,img._depth,img._spectrum,img._data,npos); - if (is_shared) - throw CImgArgumentException(_cimglist_instance - "insert(): Invalid insertion request of specified shared image " - "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).", - cimglist_instance, - img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos); - - CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1): - (_allocated_width=16)]:0; - if (!_data) { // Insert new element into empty list - _data = new_data; - *_data = img; - } else { - if (new_data) { // Insert with re-allocation - if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos); - if (npos!=_width - 1) - std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos)); - std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1)); - delete[] _data; - _data = new_data; - } else if (npos!=_width - 1) // Insert without re-allocation - std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos)); - _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; - _data[npos]._data = 0; - _data[npos] = img; - } - return *this; - } - - //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization. - CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) { - const unsigned int npos = pos==~0U?_width:pos; - if (npos>_width) - throw CImgArgumentException(_cimglist_instance - "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " - "at position %u.", - cimglist_instance, - img._width,img._height,img._depth,img._spectrum,img._data,npos); - CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1): - (_allocated_width=16)]:0; - if (!_data) { // Insert new element into empty list - _data = new_data; - if (is_shared && img) { - _data->_width = img._width; - _data->_height = img._height; - _data->_depth = img._depth; - _data->_spectrum = img._spectrum; - _data->_is_shared = true; - _data->_data = img._data; - } else *_data = img; - } - else { - if (new_data) { // Insert with re-allocation - if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos); - if (npos!=_width - 1) - std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos)); - if (is_shared && img) { - new_data[npos]._width = img._width; - new_data[npos]._height = img._height; - new_data[npos]._depth = img._depth; - new_data[npos]._spectrum = img._spectrum; - new_data[npos]._is_shared = true; - new_data[npos]._data = img._data; - } else { - new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; - new_data[npos]._data = 0; - new_data[npos] = img; - } - std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1)); - delete[] _data; - _data = new_data; - } else { // Insert without re-allocation - if (npos!=_width - 1) - std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos)); - if (is_shared && img) { - _data[npos]._width = img._width; - _data[npos]._height = img._height; - _data[npos]._depth = img._depth; - _data[npos]._spectrum = img._spectrum; - _data[npos]._is_shared = true; - _data[npos]._data = img._data; - } else { - _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; - _data[npos]._data = 0; - _data[npos] = img; - } - } - } - return *this; - } - - //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance. - template<typename t> - CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const { - return (+*this).insert(img,pos,is_shared); - } - - //! Insert n empty images img into the current image list, at position \p pos. - /** - \param n Number of empty images to insert. - \param pos Index of the insertion. - **/ - CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) { - CImg<T> empty; - if (!n) return *this; - const unsigned int npos = pos==~0U?_width:pos; - for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i); - return *this; - } - - //! Insert n empty images img into the current image list, at position \p pos \newinstance. - CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const { - return (+*this).insert(n,pos); - } - - //! Insert \c n copies of the image \c img into the current image list, at position \c pos. - /** - \param n Number of image copies to insert. - \param img Image to insert by copy. - \param pos Index of the insertion. - \param is_shared Tells if inserted images are shared copies of \c img or not. - **/ - template<typename t> - CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, - const bool is_shared=false) { - if (!n) return *this; - const unsigned int npos = pos==~0U?_width:pos; - insert(img,npos,is_shared); - for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared); - return *this; - } - - //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance. - template<typename t> - CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, - const bool is_shared=false) const { - return (+*this).insert(n,img,pos,is_shared); - } - - //! Insert a copy of the image list \c list into the current image list, starting from position \c pos. - /** - \param list Image list to insert. - \param pos Index of the insertion. - \param is_shared Tells if inserted images are shared copies of images of \c list or not. - **/ - template<typename t> - CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) { - const unsigned int npos = pos==~0U?_width:pos; - if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared); - else insert(CImgList<T>(list),npos,is_shared); - return *this; - } - - //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance. - template<typename t> - CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const { - return (+*this).insert(list,pos,is_shared); - } - - //! Insert n copies of the list \c list at position \c pos of the current list. - /** - \param n Number of list copies to insert. - \param list Image list to insert. - \param pos Index of the insertion. - \param is_shared Tells if inserted images are shared copies of images of \c list or not. - **/ - template<typename t> - CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, - const bool is_shared=false) { - if (!n) return *this; - const unsigned int npos = pos==~0U?_width:pos; - for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared); - return *this; - } - - //! Insert n copies of the list \c list at position \c pos of the current list \newinstance. - template<typename t> - CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, - const bool is_shared=false) const { - return (+*this).insert(n,list,pos,is_shared); - } - - //! Remove all images between from indexes. - /** - \param pos1 Starting index of the removal. - \param pos2 Ending index of the removal. - **/ - CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) { - const unsigned int - npos1 = pos1<pos2?pos1:pos2, - tpos2 = pos1<pos2?pos2:pos1, - npos2 = tpos2<_width?tpos2:_width - 1; - if (npos1>=_width) - throw CImgArgumentException(_cimglist_instance - "remove(): Invalid remove request at positions %u->%u.", - cimglist_instance, - npos1,tpos2); - else { - if (tpos2>=_width) - throw CImgArgumentException(_cimglist_instance - "remove(): Invalid remove request at positions %u->%u.", - cimglist_instance, - npos1,tpos2); - - for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign(); - const unsigned int nb = 1 + npos2 - npos1; - if (!(_width-=nb)) return assign(); - if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation - if (npos1!=_width) - std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1)); - std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb); - } else { // Removing items with reallocation - _allocated_width>>=2; - while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1; - CImg<T> *const new_data = new CImg<T>[_allocated_width]; - if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1); - if (npos1!=_width) - std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1)); - if (_width!=_allocated_width) - std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width)); - std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb)); - delete[] _data; - _data = new_data; - } - } - return *this; - } - - //! Remove all images between from indexes \newinstance. - CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const { - return (+*this).remove(pos1,pos2); - } - - //! Remove image at index \c pos from the image list. - /** - \param pos Index of the image to remove. - **/ - CImgList<T>& remove(const unsigned int pos) { - return remove(pos,pos); - } - - //! Remove image at index \c pos from the image list \newinstance. - CImgList<T> get_remove(const unsigned int pos) const { - return (+*this).remove(pos); - } - - //! Remove last image. - /** - **/ - CImgList<T>& remove() { - return remove(_width - 1); - } - - //! Remove last image \newinstance. - CImgList<T> get_remove() const { - return (+*this).remove(); - } - - //! Reverse list order. - CImgList<T>& reverse() { - for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]); - return *this; - } - - //! Reverse list order \newinstance. - CImgList<T> get_reverse() const { - return (+*this).reverse(); - } - - //! Return a sublist. - /** - \param pos0 Starting index of the sublist. - \param pos1 Ending index of the sublist. - **/ - CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) { - return get_images(pos0,pos1).move_to(*this); - } - - //! Return a sublist \newinstance. - CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const { - if (pos0>pos1 || pos1>=_width) - throw CImgArgumentException(_cimglist_instance - "images(): Specified sub-list indices (%u->%u) are out of bounds.", - cimglist_instance, - pos0,pos1); - CImgList<T> res(pos1 - pos0 + 1); - cimglist_for(res,l) res[l].assign(_data[pos0 + l]); - return res; - } - - //! Return a shared sublist. - /** - \param pos0 Starting index of the sublist. - \param pos1 Ending index of the sublist. - **/ - CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) { - if (pos0>pos1 || pos1>=_width) - throw CImgArgumentException(_cimglist_instance - "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", - cimglist_instance, - pos0,pos1); - CImgList<T> res(pos1 - pos0 + 1); - cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); - return res; - } - - //! Return a shared sublist \newinstance. - const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const { - if (pos0>pos1 || pos1>=_width) - throw CImgArgumentException(_cimglist_instance - "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", - cimglist_instance, - pos0,pos1); - CImgList<T> res(pos1 - pos0 + 1); - cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); - return res; - } - - //! Return a single image which is the appending of all images of the current CImgList instance. - /** - \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignment. - **/ - CImg<T> get_append(const char axis, const float align=0) const { - if (is_empty()) return CImg<T>(); - if (_width==1) return +((*this)[0]); - unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0; - CImg<T> res; - switch (cimg::lowercase(axis)) { - case 'x' : { // Along the X-axis - cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) { - dx+=img._width; - dy = std::max(dy,img._height); - dz = std::max(dz,img._depth); - dc = std::max(dc,img._spectrum); - } - } - res.assign(dx,dy,dz,dc,(T)0); - if (res) cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) res.draw_image(pos, - (int)(align*(dy - img._height)), - (int)(align*(dz - img._depth)), - (int)(align*(dc - img._spectrum)), - img); - pos+=img._width; - } - } break; - case 'y' : { // Along the Y-axis - cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) { - dx = std::max(dx,img._width); - dy+=img._height; - dz = std::max(dz,img._depth); - dc = std::max(dc,img._spectrum); - } - } - res.assign(dx,dy,dz,dc,(T)0); - if (res) cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) res.draw_image((int)(align*(dx - img._width)), - pos, - (int)(align*(dz - img._depth)), - (int)(align*(dc - img._spectrum)), - img); - pos+=img._height; - } - } break; - case 'z' : { // Along the Z-axis - cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) { - dx = std::max(dx,img._width); - dy = std::max(dy,img._height); - dz+=img._depth; - dc = std::max(dc,img._spectrum); - } - } - res.assign(dx,dy,dz,dc,(T)0); - if (res) cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) res.draw_image((int)(align*(dx - img._width)), - (int)(align*(dy - img._height)), - pos, - (int)(align*(dc - img._spectrum)), - img); - pos+=img._depth; - } - } break; - default : { // Along the C-axis - cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) { - dx = std::max(dx,img._width); - dy = std::max(dy,img._height); - dz = std::max(dz,img._depth); - dc+=img._spectrum; - } - } - res.assign(dx,dy,dz,dc,(T)0); - if (res) cimglist_for(*this,l) { - const CImg<T>& img = (*this)[l]; - if (img) res.draw_image((int)(align*(dx - img._width)), - (int)(align*(dy - img._height)), - (int)(align*(dz - img._depth)), - pos, - img); - pos+=img._spectrum; - } - } - } - return res; - } - - //! Return a list where each image has been split along the specified axis. - /** - \param axis Axis to split images along. - \param nb Number of spliting parts for each image. - **/ - CImgList<T>& split(const char axis, const int nb=-1) { - return get_split(axis,nb).move_to(*this); - } - - //! Return a list where each image has been split along the specified axis \newinstance. - CImgList<T> get_split(const char axis, const int nb=-1) const { - CImgList<T> res; - cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U); - return res; - } - - //! Insert image at the end of the list. - /** - \param img Image to insert. - **/ - template<typename t> - CImgList<T>& push_back(const CImg<t>& img) { - return insert(img); - } - - //! Insert image at the front of the list. - /** - \param img Image to insert. - **/ - template<typename t> - CImgList<T>& push_front(const CImg<t>& img) { - return insert(img,0); - } - - //! Insert list at the end of the current list. - /** - \param list List to insert. - **/ - template<typename t> - CImgList<T>& push_back(const CImgList<t>& list) { - return insert(list); - } - - //! Insert list at the front of the current list. - /** - \param list List to insert. - **/ - template<typename t> - CImgList<T>& push_front(const CImgList<t>& list) { - return insert(list,0); - } - - //! Remove last image. - /** - **/ - CImgList<T>& pop_back() { - return remove(_width - 1); - } - - //! Remove first image. - /** - **/ - CImgList<T>& pop_front() { - return remove(0); - } - - //! Remove image pointed by iterator. - /** - \param iter Iterator pointing to the image to remove. - **/ - CImgList<T>& erase(const iterator iter) { - return remove(iter - _data); - } - - //@} - //---------------------------------- - // - //! \name Data Input - //@{ - //---------------------------------- - - //! Display a simple interactive interface to select images or sublists. - /** - \param disp Window instance to display selection and user interface. - \param feature_type Can be \c false to select a single image, or \c true to select a sublist. - \param axis Axis along whom images are appended for visualization. - \param align Alignment setting when images have not all the same size. - \param exit_on_anykey Exit function when any key is pressed. - \return A one-column vector containing the selected image indexes. - **/ - CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true, - const char axis='x', const float align=0, - const bool exit_on_anykey=false) const { - return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false); - } - - //! Display a simple interactive interface to select images or sublists. - /** - \param title Title of a new window used to display selection and user interface. - \param feature_type Can be \c false to select a single image, or \c true to select a sublist. - \param axis Axis along whom images are appended for visualization. - \param align Alignment setting when images have not all the same size. - \param exit_on_anykey Exit function when any key is pressed. - \return A one-column vector containing the selected image indexes. - **/ - CImg<intT> get_select(const char *const title, const bool feature_type=true, - const char axis='x', const float align=0, - const bool exit_on_anykey=false) const { - CImgDisplay disp; - return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false); - } - - CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type, - const char axis, const float align, const bool exit_on_anykey, - const unsigned int orig, const bool resize_disp, - const bool exit_on_rightbutton, const bool exit_on_wheel) const { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "select(): Empty instance.", - cimglist_instance); - - // Create image correspondence table and get list dimensions for visualization. - CImgList<uintT> _indices; - unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0; - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - const unsigned int - w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), - h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); - if (w>max_width) max_width = w; - if (h>max_height) max_height = h; - sum_width+=w; sum_height+=h; - if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices); - else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices); - } - const CImg<uintT> indices0 = _indices>'x'; - - // Create display window. - if (!disp) { - if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1); - else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1); - if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); - } else if (title) disp.set_title("%s",title); - if (resize_disp) { - if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false); - else disp.resize(cimg_fitscreen(max_width,sum_height,1),false); - } - - const unsigned int old_normalization = disp.normalization(); - bool old_is_resized = disp.is_resized(); - disp._normalization = 0; - disp.show().set_key(0); - static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; - - // Enter event loop. - CImg<ucharT> visu0, visu; - CImg<uintT> indices; - CImg<intT> positions(_width,4,1,1,-1); - int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1; - bool is_clicked = false, is_selected = false, text_down = false, update_display = true; - unsigned int key = 0; - - while (!is_selected && !disp.is_closed() && !key) { - - // Create background image. - if (!visu0) { - visu0.assign(disp._width,disp._height,1,3,0); visu.assign(); - (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices); - unsigned int ind = 0; - const CImg<T> onexone(1,1,1,1,(T)0); - if (axis=='x') - cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) - cimglist_for(*this,ind) { - unsigned int x0 = 0; - while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {} - unsigned int x1 = x0; - while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {} - const CImg<T> &src = _data[ind]?_data[ind]:onexone; - CImg<ucharT> res; - src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2). - move_to(res); - const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true); - res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100); - positions(ind,0) = positions(ind,2) = (int)x0; - positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height())); - positions(ind,2)+=res._width; - positions(ind,3)+=res._height - 1; - visu0.draw_image(positions(ind,0),positions(ind,1),res); - } - else - cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) - cimglist_for(*this,ind) { - unsigned int y0 = 0; - while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {} - unsigned int y1 = y0; - while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {} - const CImg<T> &src = _data[ind]?_data[ind]:onexone; - CImg<ucharT> res; - src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2). - move_to(res); - const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false); - res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100); - positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width())); - positions(ind,1) = positions(ind,3) = (int)y0; - positions(ind,2)+=res._width - 1; - positions(ind,3)+=res._height; - visu0.draw_image(positions(ind,0),positions(ind,1),res); - } - if (axis=='x') --positions(ind,2); else --positions(ind,3); - update_display = true; - } - - if (!visu || oindice0!=indice0 || oindice1!=indice1) { - if (indice0>=0 && indice1>=0) { - visu.assign(visu0,false); - const int indm = std::min(indice0,indice1), indM = std::max(indice0,indice1); - for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) { - visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), - background_color,0.2f); - if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) || - (axis!='x' && positions(ind,3) - positions(ind,1)>=8)) - visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), - foreground_color,0.9f,0xAAAAAAAA); - } - if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u",text_down, - orig + indm,orig + indM,indM - indm + 1); - else visu.__draw_text(" Image #%u (%u,%u,%u,%u)",text_down, - orig + indice0, - _data[indice0]._width, - _data[indice0]._height, - _data[indice0]._depth, - _data[indice0]._spectrum); - update_display = true; - } else visu.assign(); - } - if (!visu) { visu.assign(visu0,true); update_display = true; } - if (update_display) { visu.display(disp); update_display = false; } - disp.wait(); - - // Manage user events. - const int xm = disp.mouse_x(), ym = disp.mouse_y(); - int indice = -1; - - if (xm>=0) { - indice = (int)indices(axis=='x'?xm:ym); - if (disp.button()&1) { - if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; } - oindice1 = indice1; indice1 = indice; - if (!feature_type) is_selected = true; - } else { - if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; } - else is_selected = true; - } - } else { - if (is_clicked) { - if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; } - else indice1 = -1; - } else indice0 = indice1 = -1; - } - - if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; } - if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; } - if (disp.wheel() && exit_on_wheel) is_selected = true; - - CImg<charT> filename(32); - switch (key = disp.key()) { -#if cimg_OS!=2 - case cimg::keyCTRLRIGHT : -#endif - case 0 : case cimg::keyCTRLLEFT : key = 0; break; - case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), - CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). - _is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.set_fullscreen(false). - resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false). - _is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; - disp.set_key(key,false); key = 0; visu0.assign(); - } break; - case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - static unsigned int snap_number = 0; - std::FILE *file; - do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - if (visu0) { - (+visu0).__draw_text(" Saving snapshot... ",text_down).display(disp); - visu0.save(filename); - (+visu0).__draw_text(" Snapshot '%s' saved. ",text_down,filename._data).display(disp); - } - disp.set_key(key,false).wait(); key = 0; - } break; - case cimg::keyO : - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - static unsigned int snap_number = 0; - std::FILE *file; - do { -#ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); -#else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); -#endif - if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+visu0).__draw_text(" Saving instance... ",text_down).display(disp); - save(filename); - (+visu0).__draw_text(" Instance '%s' saved. ",text_down,filename._data).display(disp); - disp.set_key(key,false).wait(); key = 0; - } break; - } - if (disp.is_resized()) { disp.resize(false); visu0.assign(); } - if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }} - else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }} - if (!exit_on_anykey && key && key!=cimg::keyESC && - (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { - key = 0; - } - } - CImg<intT> res(1,2,1,1,-1); - if (is_selected) { - if (feature_type) res.fill(std::min(indice0,indice1),std::max(indice0,indice1)); - else res.fill(indice0); - } - if (!(disp.button()&2)) disp.set_button(); - disp._normalization = old_normalization; - disp._is_resized = old_is_resized; - disp.set_key(key); - return res; - } - - //! Load a list from a file. - /** - \param filename Filename to read data from. - **/ - CImgList<T>& load(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "load(): Specified filename is (null).", - cimglist_instance); - - if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { - CImg<charT> filename_local(256); - load(cimg::load_network(filename,filename_local)); - std::remove(filename_local); - return *this; - } - - const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.'); - const char *const ext = cimg::split_filename(filename); - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - bool is_loaded = true; - try { -#ifdef cimglist_load_plugin - cimglist_load_plugin(filename); -#endif -#ifdef cimglist_load_plugin1 - cimglist_load_plugin1(filename); -#endif -#ifdef cimglist_load_plugin2 - cimglist_load_plugin2(filename); -#endif -#ifdef cimglist_load_plugin3 - cimglist_load_plugin3(filename); -#endif -#ifdef cimglist_load_plugin4 - cimglist_load_plugin4(filename); -#endif -#ifdef cimglist_load_plugin5 - cimglist_load_plugin5(filename); -#endif -#ifdef cimglist_load_plugin6 - cimglist_load_plugin6(filename); -#endif -#ifdef cimglist_load_plugin7 - cimglist_load_plugin7(filename); -#endif -#ifdef cimglist_load_plugin8 - cimglist_load_plugin8(filename); -#endif - if (!cimg::strcasecmp(ext,"tif") || - !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); - else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); - else if (!cimg::strcasecmp(ext,"cimg") || - !cimg::strcasecmp(ext,"cimgz") || - !*ext) load_cimg(filename); - else if (!cimg::strcasecmp(ext,"rec") || - !cimg::strcasecmp(ext,"par")) load_parrec(filename); - else if (!cimg::strcasecmp(ext,"avi") || - !cimg::strcasecmp(ext,"mov") || - !cimg::strcasecmp(ext,"asf") || - !cimg::strcasecmp(ext,"divx") || - !cimg::strcasecmp(ext,"flv") || - !cimg::strcasecmp(ext,"mpg") || - !cimg::strcasecmp(ext,"m1v") || - !cimg::strcasecmp(ext,"m2v") || - !cimg::strcasecmp(ext,"m4v") || - !cimg::strcasecmp(ext,"mjp") || - !cimg::strcasecmp(ext,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !cimg::strcasecmp(ext,"qt") || - !cimg::strcasecmp(ext,"rm") || - !cimg::strcasecmp(ext,"vob") || - !cimg::strcasecmp(ext,"wmv") || - !cimg::strcasecmp(ext,"xvid") || - !cimg::strcasecmp(ext,"mpeg")) load_video(filename); - else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); - else is_loaded = false; - } catch (CImgIOException&) { is_loaded = false; } - - // If nothing loaded, try to guess file format from magic number in file. - if (!is_loaded && !is_stdin) { - std::FILE *const file = cimg::std_fopen(filename,"rb"); - if (!file) { - cimg::exception_mode(omode); - throw CImgIOException(_cimglist_instance - "load(): Failed to open file '%s'.", - cimglist_instance, - filename); - } - - const char *const f_type = cimg::ftype(file,filename); - cimg::fclose(file); - is_loaded = true; - try { - if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); - else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); - else is_loaded = false; - } catch (CImgIOException&) { is_loaded = false; } - } - - // If nothing loaded, try to load file as a single image. - if (!is_loaded) { - assign(1); - try { - _data->load(filename); - } catch (CImgIOException&) { - cimg::exception_mode(omode); - throw CImgIOException(_cimglist_instance - "load(): Failed to recognize format of file '%s'.", - cimglist_instance, - filename); - } - } - cimg::exception_mode(omode); - return *this; - } - - //! Load a list from a file \newinstance. - static CImgList<T> get_load(const char *const filename) { - return CImgList<T>().load(filename); - } - - //! Load a list from a .cimg file. - /** - \param filename Filename to read data from. - **/ - CImgList<T>& load_cimg(const char *const filename) { - return _load_cimg(0,filename); - } - - //! Load a list from a .cimg file \newinstance. - static CImgList<T> get_load_cimg(const char *const filename) { - return CImgList<T>().load_cimg(filename); - } - - //! Load a list from a .cimg file. - /** - \param file File to read data from. - **/ - CImgList<T>& load_cimg(std::FILE *const file) { - return _load_cimg(file,0); - } - - //! Load a list from a .cimg file \newinstance. - static CImgList<T> get_load_cimg(std::FILE *const file) { - return CImgList<T>().load_cimg(file); - } - - CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) { -#ifdef cimg_use_zlib -#define _cimgz_load_cimg_case(Tss) { \ - Bytef *const cbuf = new Bytef[csiz]; \ - cimg::fread(cbuf,csiz,nfile); \ - raw.assign(W,H,D,C); \ - uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \ - uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ - delete[] cbuf; \ - if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ - raw.move_to(img); \ -} -#else -#define _cimgz_load_cimg_case(Tss) \ - throw CImgIOException(_cimglist_instance \ - "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \ - cimglist_instance, \ - filename?filename:"(FILE*)"); -#endif - -#define _cimg_load_cimg_case(Ts,Tss) \ - if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ - for (unsigned int l = 0; l<N; ++l) { \ - j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \ - W = H = D = C = 0; csiz = 0; \ - if ((err = cimg_sscanf(tmp,"%u %u %u %u #%lu",&W,&H,&D,&C,&csiz))<4) \ - throw CImgIOException(_cimglist_instance \ - "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \ - cimglist_instance, \ - W,H,D,C,l,filename?filename:("(FILE*)")); \ - if (W*H*D*C>0) { \ - CImg<Tss> raw; \ - CImg<T> &img = _data[l]; \ - if (err==5) _cimgz_load_cimg_case(Tss) \ - else { \ - img.assign(W,H,D,C); \ - T *ptrd = img._data; \ - for (ulongT to_read = img.size(); to_read; ) { \ - raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ - cimg::fread(raw._data,raw._width,nfile); \ - if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ - const Tss *ptrs = raw._data; \ - for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ - to_read-=raw._width; \ - } \ - } \ - } \ - } \ - loaded = true; \ - } - - if (!filename && !file) - throw CImgArgumentException(_cimglist_instance - "load_cimg(): Specified filename is (null).", - cimglist_instance); - - const ulongT cimg_iobuffer = (ulongT)24*1024*1024; - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - bool loaded = false, endian = cimg::endianness(); - CImg<charT> tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N = 0, W, H, D, C; - unsigned long csiz; - int i, err; - do { - j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; - } while (*tmp=='#' && i>=0); - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", - &N,str_pixeltype._data,str_endian._data); - if (err<2) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimglist_instance - "load_cimg(): CImg header not found in file '%s'.", - cimglist_instance, - filename?filename:"(FILE*)"); - } - if (!cimg::strncasecmp("little",str_endian,6)) endian = false; - else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; - assign(N); - _cimg_load_cimg_case("bool",bool); - _cimg_load_cimg_case("unsigned_char",unsigned char); - _cimg_load_cimg_case("uchar",unsigned char); - _cimg_load_cimg_case("char",char); - _cimg_load_cimg_case("unsigned_short",unsigned short); - _cimg_load_cimg_case("ushort",unsigned short); - _cimg_load_cimg_case("short",short); - _cimg_load_cimg_case("unsigned_int",unsigned int); - _cimg_load_cimg_case("uint",unsigned int); - _cimg_load_cimg_case("int",int); - _cimg_load_cimg_case("unsigned_long",ulongT); - _cimg_load_cimg_case("ulong",ulongT); - _cimg_load_cimg_case("long",longT); - _cimg_load_cimg_case("unsigned_int64",uint64T); - _cimg_load_cimg_case("uint64",uint64T); - _cimg_load_cimg_case("int64",int64T); - _cimg_load_cimg_case("float",float); - _cimg_load_cimg_case("double",double); - - if (!loaded) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimglist_instance - "load_cimg(): Unsupported pixel type '%s' for file '%s'.", - cimglist_instance, - str_pixeltype._data,filename?filename:"(FILE*)"); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load a sublist list from a (non compressed) .cimg file. - /** - \param filename Filename to read data from. - \param n0 Starting index of images to read (~0U for max). - \param n1 Ending index of images to read (~0U for max). - \param x0 Starting X-coordinates of image regions to read. - \param y0 Starting Y-coordinates of image regions to read. - \param z0 Starting Z-coordinates of image regions to read. - \param c0 Starting C-coordinates of image regions to read. - \param x1 Ending X-coordinates of image regions to read (~0U for max). - \param y1 Ending Y-coordinates of image regions to read (~0U for max). - \param z1 Ending Z-coordinates of image regions to read (~0U for max). - \param c1 Ending C-coordinates of image regions to read (~0U for max). - **/ - CImgList<T>& load_cimg(const char *const filename, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1) { - return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); - } - - //! Load a sublist list from a (non compressed) .cimg file \newinstance. - static CImgList<T> get_load_cimg(const char *const filename, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1) { - return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); - } - - //! Load a sub-image list from a (non compressed) .cimg file \overloading. - CImgList<T>& load_cimg(std::FILE *const file, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1) { - return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); - } - - //! Load a sub-image list from a (non compressed) .cimg file \newinstance. - static CImgList<T> get_load_cimg(std::FILE *const file, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1) { - return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); - } - - CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename, - const unsigned int n0, const unsigned int n1, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0, - const unsigned int x1, const unsigned int y1, - const unsigned int z1, const unsigned int c1) { -#define _cimg_load_cimg_case2(Ts,Tss) \ - if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ - for (unsigned int l = 0; l<=nn1; ++l) { \ - j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \ - W = H = D = C = 0; \ - if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \ - throw CImgIOException(_cimglist_instance \ - "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \ - cimglist_instance, \ - W,H,D,C,l,filename?filename:"(FILE*)"); \ - if (W*H*D*C>0) { \ - if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ - else { \ - const unsigned int \ - _nx1 = nx1==~0U?W - 1:nx1, \ - _ny1 = ny1==~0U?H - 1:ny1, \ - _nz1 = nz1==~0U?D - 1:nz1, \ - _nc1 = nc1==~0U?C - 1:nc1; \ - if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \ - throw CImgArgumentException(_cimglist_instance \ - "load_cimg(): Invalid specified coordinates " \ - "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \ - "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \ - cimglist_instance, \ - n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \ - CImg<Tss> raw(1 + _nx1 - nx0); \ - CImg<T> &img = _data[l - nn0]; \ - img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \ - T *ptrd = img._data; \ - ulongT skipvb = nc0*W*H*D*sizeof(Tss); \ - if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ - for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \ - const ulongT skipzb = nz0*W*H*sizeof(Tss); \ - if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ - for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \ - const ulongT skipyb = ny0*W*sizeof(Tss); \ - if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ - for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \ - const ulongT skipxb = nx0*sizeof(Tss); \ - if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ - cimg::fread(raw._data,raw._width,nfile); \ - if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \ - const Tss *ptrs = raw._data; \ - for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ - const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \ - if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ - } \ - const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \ - if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ - } \ - const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \ - if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ - } \ - const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \ - if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ - } \ - } \ - } \ - loaded = true; \ - } - - if (!filename && !file) - throw CImgArgumentException(_cimglist_instance - "load_cimg(): Specified filename is (null).", - cimglist_instance); - unsigned int - nn0 = std::min(n0,n1), nn1 = std::max(n0,n1), - nx0 = std::min(x0,x1), nx1 = std::max(x0,x1), - ny0 = std::min(y0,y1), ny1 = std::max(y0,y1), - nz0 = std::min(z0,z1), nz1 = std::max(z0,z1), - nc0 = std::min(c0,c1), nc1 = std::max(c0,c1); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - bool loaded = false, endian = cimg::endianness(); - CImg<charT> tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N, W, H, D, C; - int i, err; - j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", - &N,str_pixeltype._data,str_endian._data); - if (err<2) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimglist_instance - "load_cimg(): CImg header not found in file '%s'.", - cimglist_instance, - filename?filename:"(FILE*)"); - } - if (!cimg::strncasecmp("little",str_endian,6)) endian = false; - else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; - nn1 = n1==~0U?N - 1:n1; - if (nn1>=N) - throw CImgArgumentException(_cimglist_instance - "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " - "because file '%s' contains only %u images.", - cimglist_instance, - n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N); - assign(1 + nn1 - n0); - _cimg_load_cimg_case2("bool",bool); - _cimg_load_cimg_case2("unsigned_char",unsigned char); - _cimg_load_cimg_case2("uchar",unsigned char); - _cimg_load_cimg_case2("char",char); - _cimg_load_cimg_case2("unsigned_short",unsigned short); - _cimg_load_cimg_case2("ushort",unsigned short); - _cimg_load_cimg_case2("short",short); - _cimg_load_cimg_case2("unsigned_int",unsigned int); - _cimg_load_cimg_case2("uint",unsigned int); - _cimg_load_cimg_case2("int",int); - _cimg_load_cimg_case2("unsigned_long",ulongT); - _cimg_load_cimg_case2("ulong",ulongT); - _cimg_load_cimg_case2("long",longT); - _cimg_load_cimg_case2("unsigned_int64",uint64T); - _cimg_load_cimg_case2("uint64",uint64T); - _cimg_load_cimg_case2("int64",int64T); - _cimg_load_cimg_case2("float",float); - _cimg_load_cimg_case2("double",double); - if (!loaded) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimglist_instance - "load_cimg(): Unsupported pixel type '%s' for file '%s'.", - cimglist_instance, - str_pixeltype._data,filename?filename:"(FILE*)"); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load a list from a PAR/REC (Philips) file. - /** - \param filename Filename to read data from. - **/ - CImgList<T>& load_parrec(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "load_parrec(): Specified filename is (null).", - cimglist_instance); - - CImg<charT> body(1024), filenamepar(1024), filenamerec(1024); - *body = *filenamepar = *filenamerec = 0; - const char *const ext = cimg::split_filename(filename,body); - if (!std::strcmp(ext,"par")) { - std::strncpy(filenamepar,filename,filenamepar._width - 1); - cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data); - } - if (!std::strcmp(ext,"PAR")) { - std::strncpy(filenamepar,filename,filenamepar._width - 1); - cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data); - } - if (!std::strcmp(ext,"rec")) { - std::strncpy(filenamerec,filename,filenamerec._width - 1); - cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data); - } - if (!std::strcmp(ext,"REC")) { - std::strncpy(filenamerec,filename,filenamerec._width - 1); - cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data); - } - std::FILE *file = cimg::fopen(filenamepar,"r"); - - // Parse header file - CImgList<floatT> st_slices; - CImgList<uintT> st_global; - CImg<charT> line(256); *line = 0; - int err; - do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.')); - do { - unsigned int sn,size_x,size_y,pixsize; - float rs,ri,ss; - err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss); - if (err==7) { - CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices); - unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {} - if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global); - else { - CImg<uintT> &vec = st_global[i]; - if (size_x>vec[0]) vec[0] = size_x; - if (size_y>vec[1]) vec[1] = size_y; - vec[2] = sn; - } - st_slices[st_slices._width - 1][7] = (float)i; - } - } while (err==7); - - // Read data - std::FILE *file2 = cimg::fopen(filenamerec,"rb"); - cimglist_for(st_global,l) { - const CImg<uintT>& vec = st_global[l]; - CImg<T>(vec[0],vec[1],vec[2]).move_to(*this); - } - - cimglist_for(st_slices,l) { - const CImg<floatT>& vec = st_slices[l]; - const unsigned int - sn = (unsigned int)vec[0] - 1, - pixsize = (unsigned int)vec[1], - size_x = (unsigned int)vec[2], - size_y = (unsigned int)vec[3], - imn = (unsigned int)vec[7]; - const float ri = vec[4], rs = vec[5], ss = vec[6]; - switch (pixsize) { - case 8 : { - CImg<ucharT> buf(size_x,size_y); - cimg::fread(buf._data,size_x*size_y,file2); - if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); - CImg<T>& img = (*this)[imn]; - cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); - } break; - case 16 : { - CImg<ushortT> buf(size_x,size_y); - cimg::fread(buf._data,size_x*size_y,file2); - if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); - CImg<T>& img = (*this)[imn]; - cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); - } break; - case 32 : { - CImg<uintT> buf(size_x,size_y); - cimg::fread(buf._data,size_x*size_y,file2); - if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); - CImg<T>& img = (*this)[imn]; - cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); - } break; - default : - cimg::fclose(file); - cimg::fclose(file2); - throw CImgIOException(_cimglist_instance - "load_parrec(): Unsupported %d-bits pixel type for file '%s'.", - cimglist_instance, - pixsize,filename); - } - } - cimg::fclose(file); - cimg::fclose(file2); - if (!_width) - throw CImgIOException(_cimglist_instance - "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.", - cimglist_instance, - filename); - return *this; - } - - //! Load a list from a PAR/REC (Philips) file \newinstance. - static CImgList<T> get_load_parrec(const char *const filename) { - return CImgList<T>().load_parrec(filename); - } - - //! Load a list from a YUV image sequence file. - /** - \param filename Filename to read data from. - \param size_x Width of the images. - \param size_y Height of the images. - \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>. - \param first_frame Index of first image frame to read. - \param last_frame Index of last image frame to read. - \param step_frame Step applied between each frame. - \param yuv2rgb Apply YUV to RGB transformation during reading. - **/ - CImgList<T>& load_yuv(const char *const filename, - const unsigned int size_x, const unsigned int size_y, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true) { - return _load_yuv(0,filename,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb); - } - - //! Load a list from a YUV image sequence file \newinstance. - static CImgList<T> get_load_yuv(const char *const filename, - const unsigned int size_x, const unsigned int size_y=1, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true) { - return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb); - } - - //! Load a list from an image sequence YUV file \overloading. - CImgList<T>& load_yuv(std::FILE *const file, - const unsigned int size_x, const unsigned int size_y, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true) { - return _load_yuv(file,0,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb); - } - - //! Load a list from an image sequence YUV file \newinstance. - static CImgList<T> get_load_yuv(std::FILE *const file, - const unsigned int size_x, const unsigned int size_y=1, - const unsigned int chroma_subsampling=444, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool yuv2rgb=true) { - return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling, - first_frame,last_frame,step_frame,yuv2rgb); - } - - CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename, - const unsigned int size_x, const unsigned int size_y, - const unsigned int chroma_subsampling, - const unsigned int first_frame, const unsigned int last_frame, - const unsigned int step_frame, const bool yuv2rgb) { - if (!filename && !file) - throw CImgArgumentException(_cimglist_instance - "load_yuv(): Specified filename is (null).", - cimglist_instance); - if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) - throw CImgArgumentException(_cimglist_instance - "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.", - cimglist_instance, - chroma_subsampling,filename?filename:"(FILE*)"); - const unsigned int - cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, - cfy = chroma_subsampling==420?2:1, - nfirst_frame = first_frame<last_frame?first_frame:last_frame, - nlast_frame = first_frame<last_frame?last_frame:first_frame, - nstep_frame = step_frame?step_frame:1; - - if (!size_x || !size_y || size_x%cfx || size_y%cfy) - throw CImgArgumentException(_cimglist_instance - "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.", - cimglist_instance, - size_x,size_y,filename?filename:"(FILE*)"); - - CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2); - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - bool stop_flag = false; - int err; - if (nfirst_frame) { - err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR); - if (err) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimglist_instance - "load_yuv(): File '%s' doesn't contain frame number %u.", - cimglist_instance, - filename?filename:"(FILE*)",nfirst_frame); - } - } - unsigned int frame; - for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) { - YUV.get_shared_channel(0).fill(0); - // *TRY* to read the luminance part, do not replace by cimg::fread! - err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile); - if (err!=(int)(YUV._width*YUV._height)) { - stop_flag = true; - if (err>0) - cimg::warn(_cimglist_instance - "load_yuv(): File '%s' contains incomplete data or given image dimensions " - "(%u,%u) are incorrect.", - cimglist_instance, - filename?filename:"(FILE*)",size_x,size_y); - } else { - UV.fill(0); - // *TRY* to read the luminance part, do not replace by cimg::fread! - err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile); - if (err!=(int)(UV.size())) { - stop_flag = true; - if (err>0) - cimg::warn(_cimglist_instance - "load_yuv(): File '%s' contains incomplete data or given image dimensions " - "(%u,%u) are incorrect.", - cimglist_instance, - filename?filename:"(FILE*)",size_x,size_y); - } else { - const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1); - ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2); - const unsigned int wd = YUV._width; - switch (chroma_subsampling) { - case 420 : - cimg_forY(UV,y) { - cimg_forX(UV,x) { - const ucharT U = *(ptrs1++), V = *(ptrs2++); - ptrd1[wd] = U; *(ptrd1)++ = U; - ptrd1[wd] = U; *(ptrd1)++ = U; - ptrd2[wd] = V; *(ptrd2)++ = V; - ptrd2[wd] = V; *(ptrd2)++ = V; - } - ptrd1+=wd; ptrd2+=wd; - } - break; - case 422 : - cimg_forXY(UV,x,y) { - const ucharT U = *(ptrs1++), V = *(ptrs2++); - *(ptrd1++) = U; *(ptrd1++) = U; - *(ptrd2++) = V; *(ptrd2++) = V; - } - break; - default : - YUV.draw_image(0,0,0,1,UV); - } - if (yuv2rgb) YUV.YCbCrtoRGB(); - insert(YUV); - if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); - } - } - } - if (is_empty()) - throw CImgIOException(_cimglist_instance - "load_yuv() : Missing data in file '%s'.", - cimglist_instance, - filename?filename:"(FILE*)"); - if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame) - cimg::warn(_cimglist_instance - "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.", - cimglist_instance, - nlast_frame,frame - 1,filename?filename:"(FILE*)"); - - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Load an image from a video file, using OpenCV library. - /** - \param filename Filename, as a C-string. - \param first_frame Index of the first frame to read. - \param last_frame Index of the last frame to read. - \param step_frame Step value for frame reading. - \note If step_frame==0, the current video stream is forced to be released (without any frames read). - **/ - CImgList<T>& load_video(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1) { -#ifndef cimg_use_opencv - if (first_frame || last_frame!=~0U || step_frame>1) - throw CImgArgumentException(_cimglist_instance - "load_video() : File '%s', arguments 'first_frame', 'last_frame' " - "and 'step_frame' can be only set when using OpenCV " - "(-Dcimg_use_opencv must be enabled).", - cimglist_instance,filename); - return load_ffmpeg_external(filename); -#else - static CvCapture *captures[32] = { 0 }; - static CImgList<charT> filenames(32); - static CImg<uintT> positions(32,1,1,1,0); - static int last_used_index = -1; - - // Detect if a video capture already exists for the specified filename. - cimg::mutex(9); - int index = -1; - if (filename) { - if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { - index = last_used_index; - } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { - index = l; break; - } - } else index = last_used_index; - cimg::mutex(9,0); - - // Release stream if needed. - if (!step_frame || (index>=0 && positions[index]>first_frame)) { - if (index>=0) { - cimg::mutex(9); - cvReleaseCapture(&captures[index]); - captures[index] = 0; filenames[index].assign(); positions[index] = 0; - if (last_used_index==index) last_used_index = -1; - index = -1; - cimg::mutex(9,0); - } else - if (filename) - cimg::warn(_cimglist_instance - "load_video() : File '%s', no opened video stream associated with filename found.", - cimglist_instance,filename); - else - cimg::warn(_cimglist_instance - "load_video() : No opened video stream found.", - cimglist_instance,filename); - if (!step_frame) return *this; - } - - // Find empty slot for capturing video stream. - if (index<0) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "load_video(): No already open video reader found. You must specify a " - "non-(null) filename argument for the first call.", - cimglist_instance); - else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } - if (index<0) - throw CImgIOException(_cimglist_instance - "load_video(): File '%s', no video reader slots available. " - "You have to release some of your previously opened videos.", - cimglist_instance,filename); - cimg::mutex(9); - captures[index] = cvCaptureFromFile(filename); - CImg<charT>::string(filename).move_to(filenames[index]); - positions[index] = 0; - cimg::mutex(9,0); - if (!captures[index]) { - filenames[index].assign(); - cimg::fclose(cimg::fopen(filename,"rb")); // Check file availability - throw CImgIOException(_cimglist_instance - "load_video(): File '%s', unable to detect format of video file.", - cimglist_instance,filename); - } - } - - cimg::mutex(9); - const unsigned int nb_frames = (unsigned int)std::max(0.,cvGetCaptureProperty(captures[index], - CV_CAP_PROP_FRAME_COUNT)); - cimg::mutex(9,0); - assign(); - - // Skip frames if necessary. - bool go_on = true; - unsigned int &pos = positions[index]; - while (pos<first_frame) { - cimg::mutex(9); - if (!cvGrabFrame(captures[index])) { cimg::mutex(9,0); go_on = false; break; } - cimg::mutex(9,0); - ++pos; - } - - // Read and convert frames. - const IplImage *src = 0; - if (go_on) { - const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame); - while (pos<=_last_frame) { - cimg::mutex(9); - src = cvQueryFrame(captures[index]); - if (src) { - CImg<T> frame(src->width,src->height,1,3); - const int step = (int)(src->widthStep - 3*src->width); - const unsigned char* ptrs = (unsigned char*)src->imageData; - T *ptr_r = frame.data(0,0,0,0), *ptr_g = frame.data(0,0,0,1), *ptr_b = frame.data(0,0,0,2); - if (step>0) cimg_forY(frame,y) { - cimg_forX(frame,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); } - ptrs+=step; - } else for (ulongT siz = (ulongT)src->width*src->height; siz; --siz) { - *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); - } - frame.move_to(*this); - ++pos; - - bool skip_failed = false; - for (unsigned int i = 1; i<step_frame && pos<=_last_frame; ++i, ++pos) - if (!cvGrabFrame(captures[index])) { skip_failed = true; break; } - if (skip_failed) src = 0; - } - cimg::mutex(9,0); - if (!src) break; - } - } - - if (!src || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary - cimg::mutex(9); - cvReleaseCapture(&captures[index]); - captures[index] = 0; - filenames[index].assign(); - positions[index] = 0; - index = -1; - cimg::mutex(9,0); - } - - cimg::mutex(9); - last_used_index = index; - cimg::mutex(9,0); - - if (is_empty()) - throw CImgIOException(_cimglist_instance - "load_video(): File '%s', unable to locate frame %u.", - cimglist_instance,filename,first_frame); - return *this; -#endif - } - - //! Load an image from a video file, using OpenCV library \newinstance. - static CImgList<T> get_load_video(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1) { - return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame); - } - - //! Load an image from a video file using the external tool 'ffmpeg'. - /** - \param filename Filename to read data from. - **/ - CImgList<T>& load_ffmpeg_external(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "load_ffmpeg_external(): Specified filename is (null).", - cimglist_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256); - std::FILE *file = 0; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); - if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data); - cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\"", - cimg::ffmpeg_path(), - CImg<charT>::string(filename)._system_strescape().data(), - CImg<charT>::string(filename_tmp2)._system_strescape().data()); - cimg::system(command,0); - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - assign(); - unsigned int i = 1; - for (bool stop_flag = false; !stop_flag; ++i) { - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i); - CImg<T> img; - try { img.load_pnm(filename_tmp2); } - catch (CImgException&) { stop_flag = true; } - if (img) { img.move_to(*this); std::remove(filename_tmp2); } - } - cimg::exception_mode(omode); - if (is_empty()) - throw CImgIOException(_cimglist_instance - "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.", - cimglist_instance, - filename); - return *this; - } - - //! Load an image from a video file using the external tool 'ffmpeg' \newinstance. - static CImgList<T> get_load_ffmpeg_external(const char *const filename) { - return CImgList<T>().load_ffmpeg_external(filename); - } - - //! Load gif file, using ImageMagick or GraphicsMagick's external tools. - /** - \param filename Filename to read data from. - **/ - CImgList<T>& load_gif_external(const char *const filename) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "load_gif_external(): Specified filename is (null).", - cimglist_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - if (!_load_gif_external(filename,false)) - if (!_load_gif_external(filename,true)) - try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); } - if (is_empty()) - throw CImgIOException(_cimglist_instance - "load_gif_external(): Failed to open file '%s'.", - cimglist_instance,filename); - return *this; - } - - CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) { - CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256); - std::FILE *file = 0; - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data); - else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data); - if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); - if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"", - cimg::graphicsmagick_path(), - CImg<charT>::string(filename)._system_strescape().data(), - CImg<charT>::string(filename_tmp)._system_strescape().data()); - else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\"", - cimg::imagemagick_path(), - CImg<charT>::string(filename)._system_strescape().data(), - CImg<charT>::string(filename_tmp)._system_strescape().data()); - cimg::system(command,0); - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - assign(); - - // Try to read a single frame gif. - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data); - CImg<T> img; - try { img.load_png(filename_tmp2); } - catch (CImgException&) { } - if (img) { img.move_to(*this); std::remove(filename_tmp2); } - else { // Try to read animated gif - unsigned int i = 0; - for (bool stop_flag = false; !stop_flag; ++i) { - if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i); - else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i); - CImg<T> img; - try { img.load_png(filename_tmp2); } - catch (CImgException&) { stop_flag = true; } - if (img) { img.move_to(*this); std::remove(filename_tmp2); } - } - } - cimg::exception_mode(omode); - return *this; - } - - //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance. - static CImgList<T> get_load_gif_external(const char *const filename) { - return CImgList<T>().load_gif_external(filename); - } - - //! Load a gzipped list, using external tool 'gunzip'. - /** - \param filename Filename to read data from. - **/ - CImgList<T>& load_gzip_external(const char *const filename) { - if (!filename) - throw CImgIOException(_cimglist_instance - "load_gzip_external(): Specified filename is (null).", - cimglist_instance); - cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists - CImg<charT> command(1024), filename_tmp(256), body(256); - const char - *ext = cimg::split_filename(filename,body), - *ext2 = cimg::split_filename(body,0); - std::FILE *file = 0; - do { - if (!cimg::strcasecmp(ext,"gz")) { - if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gunzip_path(), - CImg<charT>::string(filename)._system_strescape().data(), - CImg<charT>::string(filename_tmp)._system_strescape().data()); - cimg::system(command); - if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { - cimg::fclose(cimg::fopen(filename,"r")); - throw CImgIOException(_cimglist_instance - "load_gzip_external(): Failed to open file '%s'.", - cimglist_instance, - filename); - - } else cimg::fclose(file); - load(filename_tmp); - std::remove(filename_tmp); - return *this; - } - - //! Load a gzipped list, using external tool 'gunzip' \newinstance. - static CImgList<T> get_load_gzip_external(const char *const filename) { - return CImgList<T>().load_gzip_external(filename); - } - - //! Load a 3D object from a .OFF file. - /** - \param filename Filename to read data from. - \param[out] primitives At return, contains the list of 3D object primitives. - \param[out] colors At return, contains the list of 3D object colors. - \return List of 3D object vertices. - **/ - template<typename tf, typename tc> - CImgList<T>& load_off(const char *const filename, - CImgList<tf>& primitives, CImgList<tc>& colors) { - return get_load_off(filename,primitives,colors).move_to(*this); - } - - //! Load a 3D object from a .OFF file \newinstance. - template<typename tf, typename tc> - static CImgList<T> get_load_off(const char *const filename, - CImgList<tf>& primitives, CImgList<tc>& colors) { - return CImg<T>().load_off(filename,primitives,colors)<'x'; - } - - //! Load images from a TIFF file. - /** - \param filename Filename to read data from. - \param first_frame Index of first image frame to read. - \param last_frame Index of last image frame to read. - \param step_frame Step applied between each frame. - \param[out] voxel_size Voxel size, as stored in the filename. - \param[out] description Description, as stored in the filename. - **/ - CImgList<T>& load_tiff(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg<charT> *const description=0) { - const unsigned int - nfirst_frame = first_frame<last_frame?first_frame:last_frame, - nstep_frame = step_frame?step_frame:1; - unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame; -#ifndef cimg_use_tiff - cimg::unused(voxel_size,description); - if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1) - throw CImgArgumentException(_cimglist_instance - "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.", - cimglist_instance, - filename); - - return assign(CImg<T>::get_load_tiff(filename)); -#else -#if cimg_verbosity<3 - TIFFSetWarningHandler(0); - TIFFSetErrorHandler(0); -#endif - TIFF *tif = TIFFOpen(filename,"r"); - if (tif) { - unsigned int nb_images = 0; - do ++nb_images; while (TIFFReadDirectory(tif)); - if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) - cimg::warn(_cimglist_instance - "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since " - "file '%s' contains %u image(s).", - cimglist_instance, - nfirst_frame,nlast_frame,nstep_frame,filename,nb_images); - - if (nfirst_frame>=nb_images) return assign(); - if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; - assign(1 + (nlast_frame - nfirst_frame)/nstep_frame); - TIFFSetDirectory(tif,0); - cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,voxel_size,description); - TIFFClose(tif); - } else throw CImgIOException(_cimglist_instance - "load_tiff(): Failed to open file '%s'.", - cimglist_instance, - filename); - return *this; -#endif - } - - //! Load a multi-page TIFF file \newinstance. - static CImgList<T> get_load_tiff(const char *const filename, - const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg<charT> *const description=0) { - return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); - } - - //@} - //---------------------------------- - // - //! \name Data Output - //@{ - //---------------------------------- - - //! Print information about the list on the standard output. - /** - \param title Label set to the information displayed. - \param display_stats Tells if image statistics must be computed and displayed. - **/ - const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const { - unsigned int msiz = 0; - cimglist_for(*this,l) msiz+=_data[l].size(); - msiz*=sizeof(T); - const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U; - CImg<charT> _title(64); - if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type()); - std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p", - cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, - cimg::t_bold,cimg::t_normal,(void*)this, - cimg::t_bold,cimg::t_normal,_width,_allocated_width, - mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), - mdisp==0?"b":(mdisp==1?"Kio":"Mio"), - cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); - if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1)); - else std::fprintf(cimg::output(),".\n"); - - char tmp[16] = { 0 }; - cimglist_for(*this,ll) { - cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll); - std::fprintf(cimg::output()," "); - _data[ll].print(tmp,display_stats); - if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); } - } - std::fflush(cimg::output()); - return *this; - } - - //! Display the current CImgList instance in an existing CImgDisplay window (by reference). - /** - \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed. - \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignmenet. - \note This function displays the list images of the current CImgList instance into an existing - CImgDisplay window. - Images of the list are appended in a single temporarly image for visualization purposes. - The function returns immediately. - **/ - const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const { - disp.display(*this,axis,align); - return *this; - } - - //! Display the current CImgList instance in a new display window. - /** - \param disp Display window. - \param display_info Tells if image information are displayed on the standard output. - \param axis Alignment axis for images viewing. - \param align Apending alignment. - \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. - \param exit_on_anykey Exit function when any key is pressed. - \note This function opens a new window with a specific title and displays the list images of the - current CImgList instance into it. - Images of the list are appended in a single temporarly image for visualization purposes. - The function returns when a key is pressed or the display window is closed by the user. - **/ - const CImgList<T>& display(CImgDisplay &disp, const bool display_info, - const char axis='x', const float align=0, - unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { - bool is_exit = false; - return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); - } - - //! Display the current CImgList instance in a new display window. - /** - \param title Title of the opening display window. - \param display_info Tells if list information must be written on standard output. - \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>. - \param align Appending alignment. - \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. - \param exit_on_anykey Exit function when any key is pressed. - **/ - const CImgList<T>& display(const char *const title=0, const bool display_info=true, - const char axis='x', const float align=0, - unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { - CImgDisplay disp; - bool is_exit = false; - return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); - } - - const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles, - const bool display_info, const char axis, const float align, unsigned int *const XYZ, - const bool exit_on_anykey, const unsigned int orig, const bool is_first_call, - bool &is_exit) const { - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "display(): Empty instance.", - cimglist_instance); - if (!disp) { - if (axis=='x') { - unsigned int sum_width = 0, max_height = 0; - cimglist_for(*this,l) { - const CImg<T> &img = _data[l]; - const unsigned int - w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), - h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); - sum_width+=w; - if (h>max_height) max_height = h; - } - disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1); - } else { - unsigned int max_width = 0, sum_height = 0; - cimglist_for(*this,l) { - const CImg<T> &img = _data[l]; - const unsigned int - w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), - h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); - if (w>max_width) max_width = w; - sum_height+=h; - } - disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1); - } - if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); - } else if (title) disp.set_title("%s",title); - else if (titles) disp.set_title("%s",titles->__display()._data); - const CImg<char> dtitle = CImg<char>::string(disp.title()); - if (display_info) print(disp.title()); - disp.show().flush(); - - if (_width==1) { - const unsigned int dw = disp._width, dh = disp._height; - if (!is_first_call) - disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false); - disp.set_title("%s (%ux%ux%ux%u)", - dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum); - _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call); - if (disp.key()) is_exit = true; - disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data()); - } else { - bool disp_resize = !is_first_call; - while (!disp.is_closed() && !is_exit) { - const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true); - disp_resize = true; - if (s[0]<0 && !disp.wheel()) { // No selections done - if (disp.button()&2) { disp.flush(); break; } - is_exit = true; - } else if (disp.wheel()) { // Zoom in/out - const int wheel = disp.wheel(); - disp.set_wheel(); - if (!is_first_call && wheel<0) break; - if (wheel>0 && _width>=4) { - const unsigned int - delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)), - ind0 = (unsigned int)std::max(0,s[0] - (int)delta), - ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta); - if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) { - const CImgList<T> sublist = get_shared_images(ind0,ind1); - CImgList<charT> t_sublist; - if (titles) t_sublist = titles->get_shared_images(ind0,ind1); - sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, - orig + ind0,false,is_exit); - } - } - } else if (s[0]!=0 || s[1]!=width() - 1) { - const CImgList<T> sublist = get_shared_images(s[0],s[1]); - CImgList<charT> t_sublist; - if (titles) t_sublist = titles->get_shared_images(s[0],s[1]); - sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, - orig + s[0],false,is_exit); - } - disp.set_title("%s",dtitle.data()); - } - } - return *this; - } - - // [internal] Return string to describe display title. - CImg<charT> __display() const { - CImg<charT> res, str; - cimglist_for(*this,l) { - CImg<charT>::string(_data[l]).move_to(str); - if (l!=width() - 1) { - str.resize(str._width + 1,1,1,1,0); - str[str._width - 2] = ','; - str[str._width - 1] = ' '; - } - res.append(str,'x'); - } - if (!res) return CImg<charT>(1,1,1,1,0).move_to(res); - cimg::strellipsize(res,128,false); - if (_width>1) { - const unsigned int l = (unsigned int)std::strlen(res); - if (res._width<=l + 16) res.resize(l + 16,1,1,1,0); - cimg_snprintf(res._data + l,16," (#%u)",_width); - } - return res; - } - - //! Save list into a file. - /** - \param filename Filename to write data to. - \param number When positive, represents an index added to the filename. Otherwise, no number is added. - \param digits Number of digits used for adding the number to the filename. - **/ - const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "save(): Specified filename is (null).", - cimglist_instance); - // Do not test for empty instances, since .cimg format is able to manage empty instances. - const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); - const char *const ext = cimg::split_filename(filename); - CImg<charT> nfilename(1024); - const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename): - filename; - -#ifdef cimglist_save_plugin - cimglist_save_plugin(fn); -#endif -#ifdef cimglist_save_plugin1 - cimglist_save_plugin1(fn); -#endif -#ifdef cimglist_save_plugin2 - cimglist_save_plugin2(fn); -#endif -#ifdef cimglist_save_plugin3 - cimglist_save_plugin3(fn); -#endif -#ifdef cimglist_save_plugin4 - cimglist_save_plugin4(fn); -#endif -#ifdef cimglist_save_plugin5 - cimglist_save_plugin5(fn); -#endif -#ifdef cimglist_save_plugin6 - cimglist_save_plugin6(fn); -#endif -#ifdef cimglist_save_plugin7 - cimglist_save_plugin7(fn); -#endif -#ifdef cimglist_save_plugin8 - cimglist_save_plugin8(fn); -#endif - if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); - else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); - else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); - else if (!cimg::strcasecmp(ext,"avi") || - !cimg::strcasecmp(ext,"mov") || - !cimg::strcasecmp(ext,"asf") || - !cimg::strcasecmp(ext,"divx") || - !cimg::strcasecmp(ext,"flv") || - !cimg::strcasecmp(ext,"mpg") || - !cimg::strcasecmp(ext,"m1v") || - !cimg::strcasecmp(ext,"m2v") || - !cimg::strcasecmp(ext,"m4v") || - !cimg::strcasecmp(ext,"mjp") || - !cimg::strcasecmp(ext,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !cimg::strcasecmp(ext,"qt") || - !cimg::strcasecmp(ext,"rm") || - !cimg::strcasecmp(ext,"vob") || - !cimg::strcasecmp(ext,"wmv") || - !cimg::strcasecmp(ext,"xvid") || - !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); -#ifdef cimg_use_tiff - else if (!cimg::strcasecmp(ext,"tif") || - !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); -#endif - else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); - else { - if (_width==1) _data[0].save(fn,-1); - else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); } - } - return *this; - } - - //! Tell if an image list can be saved as one single file. - /** - \param filename Filename, as a C-string. - \return \c true if the file format supports multiple images, \c false otherwise. - **/ - static bool is_saveable(const char *const filename) { - const char *const ext = cimg::split_filename(filename); - if (!cimg::strcasecmp(ext,"cimgz") || -#ifdef cimg_use_tiff - !cimg::strcasecmp(ext,"tif") || - !cimg::strcasecmp(ext,"tiff") || -#endif - !cimg::strcasecmp(ext,"yuv") || - !cimg::strcasecmp(ext,"avi") || - !cimg::strcasecmp(ext,"mov") || - !cimg::strcasecmp(ext,"asf") || - !cimg::strcasecmp(ext,"divx") || - !cimg::strcasecmp(ext,"flv") || - !cimg::strcasecmp(ext,"mpg") || - !cimg::strcasecmp(ext,"m1v") || - !cimg::strcasecmp(ext,"m2v") || - !cimg::strcasecmp(ext,"m4v") || - !cimg::strcasecmp(ext,"mjp") || - !cimg::strcasecmp(ext,"mp4") || - !cimg::strcasecmp(ext,"mkv") || - !cimg::strcasecmp(ext,"mpe") || - !cimg::strcasecmp(ext,"movie") || - !cimg::strcasecmp(ext,"ogm") || - !cimg::strcasecmp(ext,"ogg") || - !cimg::strcasecmp(ext,"ogv") || - !cimg::strcasecmp(ext,"qt") || - !cimg::strcasecmp(ext,"rm") || - !cimg::strcasecmp(ext,"vob") || - !cimg::strcasecmp(ext,"wmv") || - !cimg::strcasecmp(ext,"xvid") || - !cimg::strcasecmp(ext,"mpeg")) return true; - return false; - } - - //! Save image sequence as a GIF animated file. - /** - \param filename Filename to write data to. - \param fps Number of desired frames per second. - \param nb_loops Number of loops (\c 0 for infinite looping). - **/ - const CImgList<T>& save_gif_external(const char *const filename, const float fps=25, - const unsigned int nb_loops=0) { - CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256); - CImgList<charT> filenames; - std::FILE *file = 0; - -#ifdef cimg_use_png -#define _cimg_save_gif_ext "png" -#else -#define _cimg_save_gif_ext "ppm" -#endif - - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_ext,filename_tmp._data); - if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); - cimglist_for(*this,l) { - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_ext,filename_tmp._data,l + 1); - CImg<charT>::string(filename_tmp2).move_to(filenames); - if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2); - else _data[l].save(filename_tmp2); - } - cimg_snprintf(command,command._width,"%s -delay %u -loop %u", - cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops); - CImg<ucharT>::string(command).move_to(filenames,0); - cimg_snprintf(command,command._width,"\"%s\"", - CImg<charT>::string(filename)._system_strescape().data()); - CImg<ucharT>::string(command).move_to(filenames); - CImg<charT> _command = filenames>'x'; - cimg_for(_command,p,char) if (!*p) *p = ' '; - _command.back() = 0; - - cimg::system(_command); - file = cimg::std_fopen(filename,"rb"); - if (!file) - throw CImgIOException(_cimglist_instance - "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.", - cimglist_instance, - filename); - else cimg::fclose(file); - cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]); - return *this; - } - - //! Save list as a YUV image sequence file. - /** - \param filename Filename to write data to. - \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>. - \param is_rgb Tells if the RGB to YUV conversion must be done for saving. - **/ - const CImgList<T>& save_yuv(const char *const filename=0, - const unsigned int chroma_subsampling=444, - const bool is_rgb=true) const { - return _save_yuv(0,filename,chroma_subsampling,is_rgb); - } - - //! Save image sequence into a YUV file. - /** - \param file File to write data to. - \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>. - \param is_rgb Tells if the RGB to YUV conversion must be done for saving. - **/ - const CImgList<T>& save_yuv(std::FILE *const file, - const unsigned int chroma_subsampling=444, - const bool is_rgb=true) const { - return _save_yuv(file,0,chroma_subsampling,is_rgb); - } - - const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename, - const unsigned int chroma_subsampling, - const bool is_rgb) const { - if (!file && !filename) - throw CImgArgumentException(_cimglist_instance - "save_yuv(): Specified filename is (null).", - cimglist_instance); - if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) - throw CImgArgumentException(_cimglist_instance - "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.", - cimglist_instance, - chroma_subsampling,filename?filename:"(FILE*)"); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - const unsigned int - cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, - cfy = chroma_subsampling==420?2:1, - w0 = (*this)[0]._width, h0 = (*this)[0]._height, - width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy); - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - cimglist_for(*this,l) { - const CImg<T> &frame = (*this)[l]; - cimg_forZ(frame,z) { - CImg<ucharT> YUV; - if (sizeof(T)==1 && !is_rgb && - frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3) - YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true); - else { - YUV = frame.get_slice(z); - if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0); - if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0); - if (is_rgb) YUV.RGBtoYCbCr(); - } - if (chroma_subsampling==444) - cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile); - else { - cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile); - CImg<ucharT> UV = YUV.get_channels(1,2); - UV.resize(UV._width/cfx,UV._height/cfy,1,2,2); - cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile); - } - } - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save list into a .cimg file. - /** - \param filename Filename to write data to. - \param is_compressed Tells if data compression must be enabled. - **/ - const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const { - return _save_cimg(0,filename,is_compressed); - } - - const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const { - if (!file && !filename) - throw CImgArgumentException(_cimglist_instance - "save_cimg(): Specified filename is (null).", - cimglist_instance); -#ifndef cimg_use_zlib - if (is_compressed) - cimg::warn(_cimglist_instance - "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, " - "saving them uncompressed.", - cimglist_instance, - filename?filename:"(FILE*)"); -#endif - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; - if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); - else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype); - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); - if (img._data) { - CImg<T> tmp; - if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } - const CImg<T>& ref = cimg::endianness()?tmp:img; - bool failed_to_compress = true; - if (is_compressed) { -#ifdef cimg_use_zlib - const ulongT siz = sizeof(T)*ref.size(); - uLongf csiz = siz + siz/100 + 16; - Bytef *const cbuf = new Bytef[csiz]; - if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) - cimg::warn(_cimglist_instance - "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.", - cimglist_instance, - filename?filename:"(FILE*)"); - else { - std::fprintf(nfile," #%lu\n",csiz); - cimg::fwrite(cbuf,csiz,nfile); - delete[] cbuf; - failed_to_compress = false; - } -#endif - } - if (failed_to_compress) { // Write in a non-compressed way - std::fputc('\n',nfile); - cimg::fwrite(ref._data,ref.size(),nfile); - } - } else std::fputc('\n',nfile); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Save list into a .cimg file. - /** - \param file File to write data to. - \param is_compressed Tells if data compression must be enabled. - **/ - const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const { - return _save_cimg(file,0,is_compressed); - } - - const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, - const unsigned int n0, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0) const { -#define _cimg_save_cimg_case(Ts,Tss) \ - if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ - for (unsigned int l = 0; l<lmax; ++l) { \ - j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \ - W = H = D = C = 0; \ - if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \ - throw CImgIOException(_cimglist_instance \ - "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \ - cimglist_instance, \ - W,H,D,C,l,filename?filename:"(FILE*)"); \ - if (W*H*D*C>0) { \ - if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ - else { \ - const CImg<T>& img = (*this)[l - n0]; \ - const T *ptrs = img._data; \ - const unsigned int \ - x1 = x0 + img._width - 1, \ - y1 = y0 + img._height - 1, \ - z1 = z0 + img._depth - 1, \ - c1 = c0 + img._spectrum - 1, \ - nx1 = x1>=W?W - 1:x1, \ - ny1 = y1>=H?H - 1:y1, \ - nz1 = z1>=D?D - 1:z1, \ - nc1 = c1>=C?C - 1:c1; \ - CImg<Tss> raw(1 + nx1 - x0); \ - const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ - if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ - for (unsigned int v = 1 + nc1 - c0; v; --v) { \ - const unsigned int skipzb = z0*W*H*sizeof(Tss); \ - if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ - for (unsigned int z = 1 + nz1 - z0; z; --z) { \ - const unsigned int skipyb = y0*W*sizeof(Tss); \ - if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ - for (unsigned int y = 1 + ny1 - y0; y; --y) { \ - const unsigned int skipxb = x0*sizeof(Tss); \ - if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ - raw.assign(ptrs, raw._width); \ - ptrs+=img._width; \ - if (endian) cimg::invert_endianness(raw._data,raw._width); \ - cimg::fwrite(raw._data,raw._width,nfile); \ - const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \ - if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ - } \ - const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \ - if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ - } \ - const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \ - if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ - } \ - const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \ - if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ - } \ - } \ - } \ - saved = true; \ - } - - if (!file && !filename) - throw CImgArgumentException(_cimglist_instance - "save_cimg(): Specified filename is (null).", - cimglist_instance); - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "save_cimg(): Empty instance, for file '%s'.", - cimglist_instance, - filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); - bool saved = false, endian = cimg::endianness(); - CImg<charT> tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N, W, H, D, C; - int i, err; - j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data); - if (err<2) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimglist_instance - "save_cimg(): CImg header not found in file '%s'.", - cimglist_instance, - filename?filename:"(FILE*)"); - } - if (!cimg::strncasecmp("little",str_endian,6)) endian = false; - else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; - const unsigned int lmax = std::min(N,n0 + _width); - _cimg_save_cimg_case("bool",bool); - _cimg_save_cimg_case("unsigned_char",unsigned char); - _cimg_save_cimg_case("uchar",unsigned char); - _cimg_save_cimg_case("char",char); - _cimg_save_cimg_case("unsigned_short",unsigned short); - _cimg_save_cimg_case("ushort",unsigned short); - _cimg_save_cimg_case("short",short); - _cimg_save_cimg_case("unsigned_int",unsigned int); - _cimg_save_cimg_case("uint",unsigned int); - _cimg_save_cimg_case("int",int); - _cimg_save_cimg_case("unsigned_int64",uint64T); - _cimg_save_cimg_case("uint64",uint64T); - _cimg_save_cimg_case("int64",int64T); - _cimg_save_cimg_case("float",float); - _cimg_save_cimg_case("double",double); - if (!saved) { - if (!file) cimg::fclose(nfile); - throw CImgIOException(_cimglist_instance - "save_cimg(): Unsupported data type '%s' for file '%s'.", - cimglist_instance, - filename?filename:"(FILE*)",str_pixeltype._data); - } - if (!file) cimg::fclose(nfile); - return *this; - } - - //! Insert the image instance into into an existing .cimg file, at specified coordinates. - /** - \param filename Filename to write data to. - \param n0 Starting index of images to write. - \param x0 Starting X-coordinates of image regions to write. - \param y0 Starting Y-coordinates of image regions to write. - \param z0 Starting Z-coordinates of image regions to write. - \param c0 Starting C-coordinates of image regions to write. - **/ - const CImgList<T>& save_cimg(const char *const filename, - const unsigned int n0, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0) const { - return _save_cimg(0,filename,n0,x0,y0,z0,c0); - } - - //! Insert the image instance into into an existing .cimg file, at specified coordinates. - /** - \param file File to write data to. - \param n0 Starting index of images to write. - \param x0 Starting X-coordinates of image regions to write. - \param y0 Starting Y-coordinates of image regions to write. - \param z0 Starting Z-coordinates of image regions to write. - \param c0 Starting C-coordinates of image regions to write. - **/ - const CImgList<T>& save_cimg(std::FILE *const file, - const unsigned int n0, - const unsigned int x0, const unsigned int y0, - const unsigned int z0, const unsigned int c0) const { - return _save_cimg(file,0,n0,x0,y0,z0,c0); - } - - static void _save_empty_cimg(std::FILE *const file, const char *const filename, - const unsigned int nb, - const unsigned int dx, const unsigned int dy, - const unsigned int dz, const unsigned int dc) { - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T); - std::fprintf(nfile,"%u %s\n",nb,pixel_type()); - for (unsigned int i=nb; i; --i) { - std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc); - for (ulongT off = siz; off; --off) std::fputc(0,nfile); - } - if (!file) cimg::fclose(nfile); - } - - //! Save empty (non-compressed) .cimg file with specified dimensions. - /** - \param filename Filename to write data to. - \param nb Number of images to write. - \param dx Width of images in the written file. - \param dy Height of images in the written file. - \param dz Depth of images in the written file. - \param dc Spectrum of images in the written file. - **/ - static void save_empty_cimg(const char *const filename, - const unsigned int nb, - const unsigned int dx, const unsigned int dy=1, - const unsigned int dz=1, const unsigned int dc=1) { - return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc); - } - - //! Save empty .cimg file with specified dimensions. - /** - \param file File to write data to. - \param nb Number of images to write. - \param dx Width of images in the written file. - \param dy Height of images in the written file. - \param dz Depth of images in the written file. - \param dc Spectrum of images in the written file. - **/ - static void save_empty_cimg(std::FILE *const file, - const unsigned int nb, - const unsigned int dx, const unsigned int dy=1, - const unsigned int dz=1, const unsigned int dc=1) { - return _save_empty_cimg(file,0,nb,dx,dy,dz,dc); - } - - //! Save list as a TIFF file. - /** - \param filename Filename to write data to. - \param compression_type Compression mode used to write data. - \param voxel_size Voxel size, to be stored in the filename. - \param description Description, to be stored in the filename. - \param use_bigtiff Allow to save big tiff files (>4Gb). - **/ - const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0, - const float *const voxel_size=0, const char *const description=0, - const bool use_bigtiff=true) const { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "save_tiff(): Specified filename is (null).", - cimglist_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - -#ifndef cimg_use_tiff - if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff); - else cimglist_for(*this,l) { - CImg<charT> nfilename(1024); - cimg::number_filename(filename,l,6,nfilename); - _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff); - } -#else - ulongT siz = 0; - cimglist_for(*this,l) siz+=_data[l].size(); - const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images - TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); - if (tif) { - for (unsigned int dir = 0, l = 0; l<_width; ++l) { - const CImg<T>& img = (*this)[l]; - cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description); - } - TIFFClose(tif); - } else - throw CImgIOException(_cimglist_instance - "save_tiff(): Failed to open stream for file '%s'.", - cimglist_instance, - filename); -#endif - return *this; - } - - //! Save list as a gzipped file, using external tool 'gzip'. - /** - \param filename Filename to write data to. - **/ - const CImgList<T>& save_gzip_external(const char *const filename) const { - if (!filename) - throw CImgIOException(_cimglist_instance - "save_gzip_external(): Specified filename is (null).", - cimglist_instance); - CImg<charT> command(1024), filename_tmp(256), body(256); - const char - *ext = cimg::split_filename(filename,body), - *ext2 = cimg::split_filename(body,0); - std::FILE *file; - do { - if (!cimg::strcasecmp(ext,"gz")) { - if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } else { - if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - } - if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); - } while (file); - - if (is_saveable(body)) { - save(filename_tmp); - cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", - cimg::gzip_path(), - CImg<charT>::string(filename_tmp)._system_strescape().data(), - CImg<charT>::string(filename)._system_strescape().data()); - cimg::system(command); - file = cimg::std_fopen(filename,"rb"); - if (!file) - throw CImgIOException(_cimglist_instance - "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", - cimglist_instance, - filename); - else cimg::fclose(file); - std::remove(filename_tmp); - } else { - CImg<charT> nfilename(1024); - cimglist_for(*this,l) { - cimg::number_filename(body,l,6,nfilename); - if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); - _data[l].save_gzip_external(nfilename); - } - } - return *this; - } - - //! Save image sequence, using the OpenCV library. - /** - \param filename Filename to write data to. - \param fps Number of frames per second. - \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). - \param keep_open Tells if the video writer associated to the specified filename - must be kept open or not (to allow frames to be added in the same file afterwards). - **/ - const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25, - const char *codec=0, const bool keep_open=false) const { -#ifndef cimg_use_opencv - cimg::unused(codec,keep_open); - return save_ffmpeg_external(filename,fps); -#else - static CvVideoWriter *writers[32] = { 0 }; - static CImgList<charT> filenames(32); - static CImg<intT> sizes(32,2,1,1,0); - static int last_used_index = -1; - - // Detect if a video writer already exists for the specified filename. - cimg::mutex(9); - int index = -1; - if (filename) { - if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { - index = last_used_index; - } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { - index = l; break; - } - } else index = last_used_index; - cimg::mutex(9,0); - - // Find empty slot for capturing video stream. - if (index<0) { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "save_video(): No already open video writer found. You must specify a " - "non-(null) filename argument for the first call.", - cimglist_instance); - else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } - if (index<0) - throw CImgIOException(_cimglist_instance - "save_video(): File '%s', no video writer slots available. " - "You have to release some of your previously opened videos.", - cimglist_instance,filename); - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "save_video(): Instance list is empty.", - cimglist_instance); - const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0; - if (!W || !H) - throw CImgInstanceException(_cimglist_instance - "save_video(): Frame [0] is an empty image.", - cimglist_instance); - -#define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x)) - - const char - *const _codec = codec && *codec?codec:cimg_OS==2?"mpeg":"mp4v", - codec0 = _cimg_docase(_codec[0]), - codec1 = _codec[0]?_cimg_docase(_codec[1]):0, - codec2 = _codec[1]?_cimg_docase(_codec[2]):0, - codec3 = _codec[2]?_cimg_docase(_codec[3]):0; - cimg::mutex(9); - writers[index] = cvCreateVideoWriter(filename,CV_FOURCC(codec0,codec1,codec2,codec3), - fps,cvSize(W,H)); - CImg<charT>::string(filename).move_to(filenames[index]); - sizes(index,0) = W; sizes(index,1) = H; - cimg::mutex(9,0); - if (!writers[index]) - throw CImgIOException(_cimglist_instance - "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.", - cimglist_instance,filename, - codec0,codec1,codec2,codec3); - } - - if (!is_empty()) { - const unsigned int W = sizes(index,0), H = sizes(index,1); - cimg::mutex(9); - IplImage *ipl = cvCreateImage(cvSize(W,H),8,3); - cimglist_for(*this,l) { - CImg<T> &src = _data[l]; - if (src.is_empty()) - cimg::warn(_cimglist_instance - "save_video(): Skip empty frame %d for file '%s'.", - cimglist_instance,l,filename); - if (src._depth>1 || src._spectrum>3) - cimg::warn(_cimglist_instance - "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). " - "Some image data may be ignored when writing frame into video file '%s'.", - cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename); - if (src._width==W && src._height==H && src._spectrum==3) { - const T *ptr_r = src.data(0,0,0,0), *ptr_g = src.data(0,0,0,1), *ptr_b = src.data(0,0,0,2); - char *ptrd = ipl->imageData; - cimg_forXY(src,x,y) { - *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++); - } - } else { - CImg<unsigned char> _src(src,false); - _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H); - _src.resize(W,H,1,3,_src._spectrum==1); - const unsigned char *ptr_r = _src.data(0,0,0,0), *ptr_g = _src.data(0,0,0,1), *ptr_b = _src.data(0,0,0,2); - char *ptrd = ipl->imageData; - cimg_forXY(_src,x,y) { - *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++); - } - } - cvWriteFrame(writers[index],ipl); - } - cvReleaseImage(&ipl); - cimg::mutex(9,0); - } - - cimg::mutex(9); - if (!keep_open) { - cvReleaseVideoWriter(&writers[index]); - writers[index] = 0; - filenames[index].assign(); - sizes(index,0) = sizes(index,1) = 0; - last_used_index = -1; - } else last_used_index = index; - cimg::mutex(9,0); - - return *this; -#endif - } - - //! Save image sequence, using the external tool 'ffmpeg'. - /** - \param filename Filename to write data to. - \param fps Number of frames per second. - \param codec Type of compression. - \param bitrate Output bitrate - **/ - const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25, - const char *const codec=0, const unsigned int bitrate=2048) const { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "save_ffmpeg_external(): Specified filename is (null).", - cimglist_instance); - if (is_empty()) { cimg::fempty(0,filename); return *this; } - - const char - *const ext = cimg::split_filename(filename), - *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video"; - - CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256); - CImgList<charT> filenames; - std::FILE *file = 0; - cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0])) - throw CImgInstanceException(_cimglist_instance - "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.", - cimglist_instance, - filename); - do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); - if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); - } while (file); - cimglist_for(*this,l) { - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1); - CImg<charT>::string(filename_tmp2).move_to(filenames); - if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filename_tmp2); - else _data[l].save_pnm(filename_tmp2); - } - cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"", - cimg::ffmpeg_path(), - CImg<charT>::string(filename_tmp)._system_strescape().data(), - _codec,bitrate,fps, - CImg<charT>::string(filename)._system_strescape().data()); - cimg::system(command); - file = cimg::std_fopen(filename,"rb"); - if (!file) - throw CImgIOException(_cimglist_instance - "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.", - cimglist_instance, - filename); - else cimg::fclose(file); - cimglist_for(*this,l) std::remove(filenames[l]); - return *this; - } - - //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer. - /** - \param is_compressed tells if zlib compression must be used for serialization - (this requires 'cimg_use_zlib' been enabled). - **/ - CImg<ucharT> get_serialize(const bool is_compressed=false) const { -#ifndef cimg_use_zlib - if (is_compressed) - cimg::warn(_cimglist_instance - "get_serialize(): Unable to compress data unless zlib is enabled, " - "storing them uncompressed.", - cimglist_instance); -#endif - CImgList<ucharT> stream; - CImg<charT> tmpstr(128); - const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; - if (std::strstr(ptype,"unsigned")==ptype) - cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); - else - cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype); - CImg<ucharT>::string(tmpstr,false).move_to(stream); - cimglist_for(*this,l) { - const CImg<T>& img = _data[l]; - cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); - CImg<ucharT>::string(tmpstr,false).move_to(stream); - if (img._data) { - CImg<T> tmp; - if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } - const CImg<T>& ref = cimg::endianness()?tmp:img; - bool failed_to_compress = true; - if (is_compressed) { -#ifdef cimg_use_zlib - const ulongT siz = sizeof(T)*ref.size(); - uLongf csiz = (ulongT)compressBound(siz); - Bytef *const cbuf = new Bytef[csiz]; - if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) - cimg::warn(_cimglist_instance - "get_serialize(): Failed to save compressed data, saving them uncompressed.", - cimglist_instance); - else { - cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz); - CImg<ucharT>::string(tmpstr,false).move_to(stream); - CImg<ucharT>(cbuf,csiz).move_to(stream); - delete[] cbuf; - failed_to_compress = false; - } -#endif - } - if (failed_to_compress) { // Write in a non-compressed way - CImg<charT>::string("\n",false).move_to(stream); - stream.insert(1); - stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true); - } - } else CImg<charT>::string("\n",false).move_to(stream); - } - cimglist_apply(stream,unroll)('y'); - return stream>'y'; - } - - //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list. - template<typename t> - static CImgList<T> get_unserialize(const CImg<t>& buffer) { -#ifdef cimg_use_zlib -#define _cimgz_unserialize_case(Tss) { \ - Bytef *cbuf = 0; \ - if (sizeof(t)!=1 || cimg::type<t>::string()==cimg::type<bool>::string()) { \ - cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \ - for (ulongT i = 0; i<csiz; ++i) *(_cbuf++) = (Bytef)*(stream++); \ - is_bytef = false; \ - } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \ - raw.assign(W,H,D,C); \ - uLongf destlen = raw.size()*sizeof(Tss); \ - uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ - if (!is_bytef) delete[] cbuf; \ - } -#else -#define _cimgz_unserialize_case(Tss) \ - throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \ - "unless zlib is enabled.", \ - pixel_type()); -#endif - -#define _cimg_unserialize_case(Ts,Tss) \ - if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ - for (unsigned int l = 0; l<N; ++l) { \ - j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \ - ++stream; tmp[j] = 0; \ - W = H = D = C = 0; csiz = 0; \ - if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \ - throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \ - "image #%u in serialized buffer.", \ - pixel_type(),W,H,D,C,l); \ - if (W*H*D*C>0) { \ - CImg<Tss> raw; \ - CImg<T> &img = res._data[l]; \ - if (err==5) _cimgz_unserialize_case(Tss) \ - else if (sizeof(Tss)==sizeof(t) && cimg::type<Tss>::is_float()==cimg::type<t>::is_float()) { \ - raw.assign((Tss*)stream,W,H,D,C,true); \ - stream+=raw.size(); \ - } else { \ - raw.assign(W,H,D,C); \ - CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \ - cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \ - } \ - if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ - raw.move_to(img); \ - } \ - } \ - loaded = true; \ - } - - if (buffer.is_empty()) - throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).", - pixel_type()); - CImgList<T> res; - const t *stream = buffer._data, *const estream = buffer._data + buffer.size(); - bool loaded = false, endian = cimg::endianness(), is_bytef = false; - CImg<charT> tmp(256), str_pixeltype(256), str_endian(256); - *tmp = *str_pixeltype = *str_endian = 0; - unsigned int j, N = 0, W, H, D, C; - uint64T csiz; - int i, err; - cimg::unused(is_bytef); - do { - j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } - ++stream; tmp[j] = 0; - } while (*tmp=='#' && stream<estream); - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", - &N,str_pixeltype._data,str_endian._data); - if (err<2) - throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.", - pixel_type()); - if (!cimg::strncasecmp("little",str_endian,6)) endian = false; - else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; - res.assign(N); - _cimg_unserialize_case("bool",bool); - _cimg_unserialize_case("unsigned_char",unsigned char); - _cimg_unserialize_case("uchar",unsigned char); - _cimg_unserialize_case("char",char); - _cimg_unserialize_case("unsigned_short",unsigned short); - _cimg_unserialize_case("ushort",unsigned short); - _cimg_unserialize_case("short",short); - _cimg_unserialize_case("unsigned_int",unsigned int); - _cimg_unserialize_case("uint",unsigned int); - _cimg_unserialize_case("int",int); - _cimg_unserialize_case("unsigned_int64",uint64T); - _cimg_unserialize_case("uint64",uint64T); - _cimg_unserialize_case("int64",int64T); - _cimg_unserialize_case("float",float); - _cimg_unserialize_case("double",double); - if (!loaded) - throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined " - "in serialized buffer.", - pixel_type(),str_pixeltype._data); - return res; - } - - //@} - //---------------------------------- - // - //! \name Others - //@{ - //---------------------------------- - - //! Return a CImg pre-defined font with requested height. - /** - \param font_height Height of the desired font (exact match for 13,23,53,103). - \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width. - **/ - static const CImgList<ucharT>& font(const unsigned int requested_height, const bool is_variable_width=true) { - if (!requested_height) return CImgList<ucharT>::const_empty(); - cimg::mutex(11); - static const unsigned char font_resizemap[] = { - 0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30, - 32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52, - 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72, - 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, - 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, - 123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, - 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165, - 166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179, - 180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192, - 193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205, - 206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218, - 219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231, - 231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, - 244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 }; - static const char *const *font_data[] = { - cimg::data_font_small, - cimg::data_font_normal, - cimg::data_font_large, - cimg::data_font_huge }; - static const unsigned int - font_width[] = { 10,26,52,104 }, - font_height[] = { 13,32,64,128 }, - font_M[] = { 86,91,91,47 }, - font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*), - sizeof(cimg::data_font_normal)/sizeof(char*), - sizeof(cimg::data_font_large)/sizeof(char*), - sizeof(cimg::data_font_huge)/sizeof(char*) }; - static const unsigned char font_is_binary[] = { 1,0,0,1 }; - static CImg<ucharT> font_base[4]; - - unsigned int ind = - requested_height<=font_height[0]?0U: - requested_height<=font_height[1]?1U: - requested_height<=font_height[2]?2U:3U; - - // Decompress nearest base font data if needed. - CImg<ucharT> &basef = font_base[ind]; - if (!basef) { - basef.assign(256*font_width[ind],font_height[ind]); - - unsigned char *ptrd = basef; - const unsigned char *const ptrde = basef.end(); - - // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb). - CImg<char> dataf; - for (unsigned int k = 0; k<font_chunk[ind]; ++k) - dataf.append(CImg<char>::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x'); - - // Uncompress font data (decode RLE). - const unsigned int M = font_M[ind]; - if (font_is_binary[ind]) - for (const char *ptrs = dataf; *ptrs; ++ptrs) { - const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n; - if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } - else { std::memset(ptrd,v,ptrde - ptrd); break; } - } - else - for (const char *ptrs = dataf; *ptrs; ++ptrs) { - int n = (int)*ptrs - M - 32, v = 0; - if (n>=0) { v = 85*n; n = 1; } - else { - n = -n; - v = (int)*(++ptrs) - M - 32; - if (v<0) { v = 0; --ptrs; } else v*=85; - } - if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } - else { std::memset(ptrd,v,ptrde - ptrd); break; } - } - } - - // Find optimal font cache location to return. - static CImgList<ucharT> fonts[16]; - static bool is_variable_widths[16] = { 0 }; - ind = ~0U; - for (int i = 0; i<16; ++i) - if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) { - ind = (unsigned int)i; break; // Found empty slot or cached font - } - if (ind==~0U) { // No empty slots nor existing font in cache - fonts->assign(); - std::memmove(fonts,fonts + 1,15*sizeof(CImgList<ucharT>)); - std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool)); - std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font - } - CImgList<ucharT> &font = fonts[ind]; - - // Render requested font. - if (!font) { - const unsigned int padding_x = requested_height<=64?1U:requested_height<=128?2U:3U; - is_variable_widths[ind] = is_variable_width; - font = basef.get_split('x',256); - if (requested_height!=font[0]._height) - cimglist_for(font,l) { - font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100, - font[0]._height>requested_height?2:5); - cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr]; - } - if (is_variable_width) { // Crop font - cimglist_for(font,l) { - CImg<ucharT>& letter = font[l]; - int xmin = letter.width(), xmax = 0; - cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; } - if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1); - } - font[' '].resize(font['f']._width,-100,-100,-100,0); - if (' ' + 256<font.size()) font[' ' + 256].resize(font['f']._width,-100,-100,-100,0); - } - cimglist_for(font,l) - font[l].resize(std::max(font[';']._width,font[l]._width) + padding_x, - -100,1,1,0,0,0.55f); - font.insert(256,0); - cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1); - } - cimg::mutex(11,0); - return font; - } - - //! Compute a 1D Fast Fourier Transform, along specified axis. - /** - \param axis Axis along which the Fourier transform is computed. - \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed. - **/ - CImgList<T>& FFT(const char axis, const bool invert=false) { - if (is_empty()) return *this; - if (_width==1) insert(1); - if (_width>2) - cimg::warn(_cimglist_instance - "FFT(): Instance has more than 2 images", - cimglist_instance); - - CImg<T>::FFT(_data[0],_data[1],axis,invert); - return *this; - } - - //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance. - CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const { - return CImgList<Tfloat>(*this,false).FFT(axis,invert); - } - - //! Compute a n-d Fast Fourier Transform. - /** - \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed. - **/ - CImgList<T>& FFT(const bool invert=false) { - if (is_empty()) return *this; - if (_width==1) insert(1); - if (_width>2) - cimg::warn(_cimglist_instance - "FFT(): Instance has more than 2 images", - cimglist_instance); - - CImg<T>::FFT(_data[0],_data[1],invert); - return *this; - } - - //! Compute a n-d Fast Fourier Transform \newinstance. - CImgList<Tfloat> get_FFT(const bool invert=false) const { - return CImgList<Tfloat>(*this,false).FFT(invert); - } - - //! Reverse primitives orientations of a 3D object. - /** - **/ - CImgList<T>& reverse_object3d() { - cimglist_for(*this,l) { - CImg<T>& p = _data[l]; - switch (p.size()) { - case 2 : case 3: cimg::swap(p[0],p[1]); break; - case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break; - case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break; - case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break; - case 12 : cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break; - } - } - return *this; - } - - //! Reverse primitives orientations of a 3D object \newinstance. - CImgList<T> get_reverse_object3d() const { - return (+*this).reverse_object3d(); - } - - //@} - }; // struct CImgList<T> { ... - - /* - #--------------------------------------------- - # - # Completion of previously declared functions - # - #---------------------------------------------- - */ - -namespace cimg { - - // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. - // (throw a CImgIOException when macro 'cimg_use_r' is defined). - inline FILE* _stdin(const bool throw_exception) { -#ifndef cimg_use_r - cimg::unused(throw_exception); - return stdin; -#else - if (throw_exception) { - cimg::exception_mode(0); - throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode " - "('cimg_use_r' is defined)."); - } - return 0; -#endif - } - - inline FILE* _stdout(const bool throw_exception) { -#ifndef cimg_use_r - cimg::unused(throw_exception); - return stdout; -#else - if (throw_exception) { - cimg::exception_mode(0); - throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode " - "('cimg_use_r' is defined)."); - } - return 0; -#endif - } - - inline FILE* _stderr(const bool throw_exception) { -#ifndef cimg_use_r - cimg::unused(throw_exception); - return stderr; -#else - if (throw_exception) { - cimg::exception_mode(0); - throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode " - "('cimg_use_r' is defined)."); - } - return 0; -#endif - } - - // Open a file (similar to std:: fopen(), but with wide character support on Windows). - inline std::FILE *std_fopen(const char *const path, const char *const mode) { - std::FILE *const res = std::fopen(path,mode); - if (res) return res; -#if cimg_OS==2 - // Try alternative method, with wide-character string. - int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); - if (err) { - CImg<wchar_t> wpath(err); - err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err); - if (err) { // Convert 'mode' to a wide-character string - err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0); - if (err) { - CImg<wchar_t> wmode(err); - if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err)) - return _wfopen(wpath,wmode); - } - } - } -#endif - return 0; - } - - //! Get/set path to store temporary files. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path where temporary files can be saved. - **/ - inline const char* temporary_path(const char *const user_path, const bool reinit_path) { -#define _cimg_test_temporary_path(p) \ - if (!path_found) { \ - cimg_snprintf(s_path,s_path.width(),"%s",p); \ - cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ - if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ - } - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - CImg<char> tmp(1024), filename_tmp(256); - std::FILE *file = 0; - cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand()); - char *tmpPath = std::getenv("TMP"); - if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); } - if (tmpPath) _cimg_test_temporary_path(tmpPath); -#if cimg_OS==2 - _cimg_test_temporary_path("C:\\WINNT\\Temp"); - _cimg_test_temporary_path("C:\\WINDOWS\\Temp"); - _cimg_test_temporary_path("C:\\Temp"); - _cimg_test_temporary_path("C:"); - _cimg_test_temporary_path("D:\\WINNT\\Temp"); - _cimg_test_temporary_path("D:\\WINDOWS\\Temp"); - _cimg_test_temporary_path("D:\\Temp"); - _cimg_test_temporary_path("D:"); -#else - _cimg_test_temporary_path("/tmp"); - _cimg_test_temporary_path("/var/tmp"); -#endif - if (!path_found) { - *s_path = 0; - std::strncpy(tmp,filename_tmp,tmp._width - 1); - if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } - } - if (!path_found) { - cimg::mutex(7,0); - throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n"); - } - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the <i>Program Files/</i> directory (Windows only). - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the program files. - **/ -#if cimg_OS==2 - inline const char* programfiles_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(MAX_PATH); - *s_path = 0; - // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). -#if !defined(__INTEL_COMPILER) - if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) { - const char *const pfPath = std::getenv("PROGRAMFILES"); - if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1); - else std::strcpy(s_path,"C:\\PROGRA~1"); - } -#else - std::strcpy(s_path,"C:\\PROGRA~1"); -#endif - } - cimg::mutex(7,0); - return s_path; - } -#endif - - //! Get/set path to the ImageMagick's \c convert binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c convert binary. - **/ - inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - const char *const pf_path = programfiles_path(); - for (int l = 0; l<2 && !path_found; ++l) { - const char *const s_exe = l?"convert":"magick"; - cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe); - } -#else - std::strcpy(s_path,"./magick"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - if (!path_found) { - std::strcpy(s_path,"./convert"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"convert"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the GraphicsMagick's \c gm binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c gm binary. - **/ - inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - const char *const pf_path = programfiles_path(); - if (!path_found) { - std::strcpy(s_path,".\\gm.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gm.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./gm"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gm"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the XMedcon's \c medcon binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c medcon binary. - **/ - inline const char* medcon_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - const char *const pf_path = programfiles_path(); - if (!path_found) { - std::strcpy(s_path,".\\medcon.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) { - cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) { - cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) { - std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"medcon.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./medcon"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"medcon"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the FFMPEG's \c ffmpeg binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c ffmpeg binary. - **/ - inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\ffmpeg.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"ffmpeg.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./ffmpeg"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"ffmpeg"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the \c gzip binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c gzip binary. - **/ - inline const char *gzip_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\gzip.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gzip.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./gzip"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gzip"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the \c gunzip binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c gunzip binary. - **/ - inline const char *gunzip_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\gunzip.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gunzip.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./gunzip"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"gunzip"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the \c dcraw binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c dcraw binary. - **/ - inline const char *dcraw_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\dcraw.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"dcraw.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./dcraw"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"dcraw"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the \c wget binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c wget binary. - **/ - inline const char *wget_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\wget.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"wget.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./wget"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"wget"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - //! Get/set path to the \c curl binary. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path containing the \c curl binary. - **/ - inline const char *curl_path(const char *const user_path, const bool reinit_path) { - static CImg<char> s_path; - cimg::mutex(7); - if (reinit_path) s_path.assign(); - if (user_path) { - if (!s_path) s_path.assign(1024); - std::strncpy(s_path,user_path,1023); - } else if (!s_path) { - s_path.assign(1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(s_path,".\\curl.exe"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"curl.exe"); -#else - if (!path_found) { - std::strcpy(s_path,"./curl"); - if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"curl"); -#endif - winformat_string(s_path); - } - cimg::mutex(7,0); - return s_path; - } - - // [internal] Sorting function, used by cimg::files(). - inline int _sort_files(const void* a, const void* b) { - const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b; - return std::strcmp(sa._data,sb._data); - } - - //! Return list of files/directories in specified directory. - /** - \param path Path to the directory. Set to 0 for current directory. - \param is_pattern Tell if specified path has a matching pattern in it. - \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }. - \param include_path Tell if \c path must be included in resulting filenames. - \return A list of filenames. - **/ - inline CImgList<char> files(const char *const path, const bool is_pattern=false, - const unsigned int mode=2, const bool include_path=false) { - if (!path || !*path) return files("*",true,mode,include_path); - CImgList<char> res; - - // If path is a valid folder name, ignore argument 'is_pattern'. - const bool _is_pattern = is_pattern && !cimg::is_directory(path); - bool is_root = false, is_current = false; - cimg::unused(is_root,is_current); - - // Clean format of input path. - CImg<char> pattern, _path = CImg<char>::string(path); -#if cimg_OS==2 - for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/'; -#endif - char *pd = _path; - for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; } - *pd = 0; - unsigned int lp = (unsigned int)std::strlen(_path); - if (!_is_pattern && lp && _path[lp - 1]=='/') { - _path[lp - 1] = 0; --lp; -#if cimg_OS!=2 - is_root = !*_path; -#endif - } - - // Separate folder path and matching pattern. - if (_is_pattern) { - const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data()); - CImg<char>::string(_path).move_to(pattern); - if (bpos) { - _path[bpos - 1] = 0; // End 'path' at last slash -#if cimg_OS!=2 - is_root = !*_path; -#endif - } else { // No path to folder specified, assuming current folder - is_current = true; *_path = 0; - } - lp = (unsigned int)std::strlen(_path); - } - - // Windows version. -#if cimg_OS==2 - if (!_is_pattern) { - pattern.assign(lp + 3); - std::memcpy(pattern,_path,lp); - pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0; - } - WIN32_FIND_DATAA file_data; - const HANDLE dir = FindFirstFileA(pattern.data(),&file_data); - if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty(); - do { - const char *const filename = file_data.cFileName; - if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { - const unsigned int lf = (unsigned int)std::strlen(filename); - const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0; - if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) { - if (include_path) { - CImg<char> full_filename((lp?lp+1:0) + lf + 1); - if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; } - std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1); - full_filename.move_to(res); - } else CImg<char>(filename,lf + 1).move_to(res); - } - } - } while (FindNextFileA(dir,&file_data)); - FindClose(dir); - - // Unix version (posix). -#elif cimg_OS == 1 - DIR *const dir = opendir(is_root?"/":is_current?".":_path.data()); - if (!dir) return CImgList<char>::const_empty(); - struct dirent *ent; - while ((ent=readdir(dir))!=0) { - const char *const filename = ent->d_name; - if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { - const unsigned int lf = (unsigned int)std::strlen(filename); - CImg<char> full_filename(lp + lf + 2); - - if (!is_current) { - full_filename.assign(lp + lf + 2); - if (lp) std::memcpy(full_filename,_path,lp); - full_filename[lp] = '/'; - std::memcpy(full_filename._data + lp + 1,filename,lf + 1); - } else full_filename.assign(filename,lf + 1); - - struct stat st; - if (stat(full_filename,&st)==-1) continue; - const bool is_directory = (st.st_mode & S_IFDIR)!=0; - if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) { - if (include_path) { - if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) - full_filename.move_to(res); - } else { - if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) - CImg<char>(filename,lf + 1).move_to(res); - } - } - } - } - closedir(dir); -#endif - - // Sort resulting list by lexicographic order. - if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files); - - return res; - } - - //! Try to guess format from an image file. - /** - \param file Input file (can be \c 0 if \c filename is set). - \param filename Filename, as a C-string (can be \c 0 if \c file is set). - \return C-string containing the guessed file format, or \c 0 if nothing has been guessed. - **/ - inline const char *ftype(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException("cimg::ftype(): Specified filename is (null)."); - static const char - *const _pnm = "pnm", - *const _pfm = "pfm", - *const _bmp = "bmp", - *const _gif = "gif", - *const _jpg = "jpg", - *const _off = "off", - *const _pan = "pan", - *const _png = "png", - *const _tif = "tif", - *const _inr = "inr", - *const _dcm = "dcm"; - const char *f_type = 0; - CImg<char> header; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - header._load_raw(file,filename,512,1,1,1,false,false,0); - const unsigned char *const uheader = (unsigned char*)header._data; - if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF - else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // INRIMAGE - else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // PANDORE - else if (!std::strncmp(header.data() + 128,"DICM",4)) f_type = _dcm; // DICOM - else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg; // JPEG - else if (header[0]=='B' && header[1]=='M') f_type = _bmp; // BMP - else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // GIF - (header[4]=='7' || header[4]=='9')) f_type = _gif; - else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && // PNG - uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png; - else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // TIFF - else { // PNM or PFM - CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false); - cimglist_for(_header,l) { - if (_header(l,0)=='#') continue; - if (_header[l]._height==2 && _header(l,0)=='P') { - const char c = _header(l,1); - if (c=='f' || c=='F') { f_type = _pfm; break; } - if (c>='1' && c<='9') { f_type = _pnm; break; } - } - f_type = 0; break; - } - } - } catch (CImgIOException&) { } - cimg::exception_mode(omode); - return f_type; - } - - //! Load file from network as a local temporary file. - /** - \param url URL of the filename, as a C-string. - \param[out] filename_local C-string containing the path to a local copy of \c filename. - \param timeout Maximum time (in seconds) authorized for downloading the file from the URL. - \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure. - \param referer Referer used, as a C-string. - \return Value of \c filename_local. - \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download. - **/ - inline char *load_network(const char *const url, char *const filename_local, - const unsigned int timeout, const bool try_fallback, - const char *const referer) { - if (!url) - throw CImgArgumentException("cimg::load_network(): Specified URL is (null)."); - if (!filename_local) - throw CImgArgumentException("cimg::load_network(): Specified destination string is (null)."); - - const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext; - CImg<char> ext = CImg<char>::string(_ext); - std::FILE *file = 0; - *filename_local = 0; - if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0; - else cimg::strwindows_reserved(ext); - do { - cimg_snprintf(filename_local,256,"%s%c%s%s", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data); - if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file); - } while (file); - -#ifdef cimg_use_curl - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - CURL *curl = 0; - CURLcode res; - curl = curl_easy_init(); - if (curl) { - file = cimg::fopen(filename_local,"wb"); - curl_easy_setopt(curl,CURLOPT_URL,url); - curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0); - curl_easy_setopt(curl,CURLOPT_WRITEDATA,file); - curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); - curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L); - curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L); - if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout); - if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L); - if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer); - res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - cimg::fseek(file,0,SEEK_END); // Check if file size is 0 - const cimg_ulong siz = cimg::ftell(file); - cimg::fclose(file); - if (siz>0 && res==CURLE_OK) { - cimg::exception_mode(omode); - return filename_local; - } else std::remove(filename_local); - } - } catch (...) { } - cimg::exception_mode(omode); - if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url); -#endif - - CImg<char> command((unsigned int)std::strlen(url) + 64); - cimg::unused(try_fallback); - - // Try with 'curl' first. - if (timeout) { - if (referer) - cimg_snprintf(command,command._width,"%s -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),referer,timeout,filename_local, - CImg<char>::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),timeout,filename_local, - CImg<char>::string(url)._system_strescape().data()); - } else { - if (referer) - cimg_snprintf(command,command._width,"%s -e %s -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),referer,filename_local, - CImg<char>::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),filename_local, - CImg<char>::string(url)._system_strescape().data()); - } - cimg::system(command); - - if (!(file=cimg::std_fopen(filename_local,"rb"))) { - - // Try with 'wget' otherwise. - if (timeout) { - if (referer) - cimg_snprintf(command,command._width,"%s --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),referer,timeout,filename_local, - CImg<char>::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),timeout,filename_local, - CImg<char>::string(url)._system_strescape().data()); - } else { - if (referer) - cimg_snprintf(command,command._width,"%s --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),referer,filename_local, - CImg<char>::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),filename_local, - CImg<char>::string(url)._system_strescape().data()); - } - cimg::system(command); - - if (!(file=cimg::std_fopen(filename_local,"rb"))) - throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands " - "'wget' or 'curl'.",url); - cimg::fclose(file); - - // Try gunzip it. - cimg_snprintf(command,command._width,"%s.gz",filename_local); - std::rename(filename_local,command); - cimg_snprintf(command,command._width,"%s --quiet \"%s.gz\"", - gunzip_path(),filename_local); - cimg::system(command); - file = cimg::std_fopen(filename_local,"rb"); - if (!file) { - cimg_snprintf(command,command._width,"%s.gz",filename_local); - std::rename(command,filename_local); - file = cimg::std_fopen(filename_local,"rb"); - } - } - cimg::fseek(file,0,SEEK_END); // Check if file size is 0 - if (std::ftell(file)<=0) - throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands " - "'wget' or 'curl'.",url); - cimg::fclose(file); - return filename_local; - } - - // Implement a tic/toc mechanism to display elapsed time of algorithms. - inline cimg_ulong tictoc(const bool is_tic) { - cimg::mutex(2); - static CImg<cimg_ulong> times(64); - static unsigned int pos = 0; - const cimg_ulong t1 = cimg::time(); - if (is_tic) { - // Tic - times[pos++] = t1; - if (pos>=times._width) - throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'."); - cimg::mutex(2,0); - return t1; - } - - // Toc - if (!pos) - throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made."); - const cimg_ulong - t0 = times[--pos], - dt = t1>=t0?(t1 - t0):cimg::type<cimg_ulong>::max(); - const unsigned int - edays = (unsigned int)(dt/86400000.), - ehours = (unsigned int)((dt - edays*86400000.)/3600000.), - emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.), - esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.), - ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.); - if (!edays && !ehours && !emin && !esec) - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n", - cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal); - else { - if (!edays && !ehours && !emin) - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n", - cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal); - else { - if (!edays && !ehours) - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n", - cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal); - else{ - if (!edays) - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n", - cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal); - else{ - std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n", - cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal); - } - } - } - } - cimg::mutex(2,0); - return dt; - } - - // Return a temporary string describing the size of a memory buffer. - inline const char *strbuffersize(const cimg_ulong size) { - static CImg<char> res(256); - cimg::mutex(5); - if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":""); - else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); } - else if (size<1024*1024*1024LU) { - const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize); - } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); } - cimg::mutex(5,0); - return res; - } - - //! Display a simple dialog box, and wait for the user's response. - /** - \param title Title of the dialog window. - \param msg Main message displayed inside the dialog window. - \param button1_label Label of the 1st button. - \param button2_label Label of the 2nd button (\c 0 to hide button). - \param button3_label Label of the 3rd button (\c 0 to hide button). - \param button4_label Label of the 4th button (\c 0 to hide button). - \param button5_label Label of the 5th button (\c 0 to hide button). - \param button6_label Label of the 6th button (\c 0 to hide button). - \param logo Image logo displayed at the left of the main message. - \param is_centered Tells if the dialog window must be centered on the screen. - \return Indice of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user. - \note - - Up to 6 buttons can be defined in the dialog window. - - The function returns when a user clicked one of the button or closed the dialog window. - - If a button text is set to 0, the corresponding button (and the followings) will not appear in the dialog box. - At least one button must be specified. - **/ - template<typename t> - inline int dialog(const char *const title, const char *const msg, - const char *const button1_label, const char *const button2_label, - const char *const button3_label, const char *const button4_label, - const char *const button5_label, const char *const button6_label, - const CImg<t>& logo, const bool is_centered=false) { -#if cimg_display==0 - cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, - logo._data,is_centered); - throw CImgIOException("cimg::dialog(): No display available."); -#else - static const unsigned char - black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; - - // Create buttons and canvas graphics - CImgList<unsigned char> buttons, cbuttons, sbuttons; - if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons); - if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons); - if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons); - if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons); - if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons); - if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons); - }}}}}} - if (!buttons._width) - throw CImgArgumentException("cimg::dialog(): No buttons have been defined."); - cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3); - - unsigned int bw = 0, bh = 0; - cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); } - bw+=8; bh+=8; - if (bw<64) bw = 64; - if (bw>128) bw = 128; - if (bh<24) bh = 24; - if (bh>48) bh = 48; - - CImg<unsigned char> button(bw,bh,1,3); - button.draw_rectangle(0,0,bw - 1,bh - 1,gray); - button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white); - button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black); - button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2); - CImg<unsigned char> sbutton(bw,bh,1,3); - sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray); - sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black); - sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black); - sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white); - sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black); - sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2); - sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); - sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); - CImg<unsigned char> cbutton(bw,bh,1,3); - cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2). - draw_rectangle(2,2,bw - 3,bh - 3,gray); - cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); - cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); - - cimglist_for(buttons,ll) { - CImg<unsigned char>(cbutton). - draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]). - move_to(cbuttons); - CImg<unsigned char>(sbutton). - draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). - move_to(sbuttons); - CImg<unsigned char>(button). - draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). - move_to(buttons[ll]); - } - - CImg<unsigned char> canvas; - if (msg) - ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas); - - const unsigned int - bwall = (buttons._width - 1)*(12 + bw) + bw, - w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall), - h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh), - lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)), - ly = (h - 12 - bh - logo._height)/2, - tx = lx + logo._width + 12, - ty = (h - 12 - bh - canvas._height)/2, - bx = (w - bwall)/2, - by = h - 12 - bh; - - if (canvas._data) - canvas = CImg<unsigned char>(w,h,1,3). - draw_rectangle(0,0,w - 1,h - 1,gray). - draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). - draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black). - draw_image(tx,ty,canvas); - else - canvas = CImg<unsigned char>(w,h,1,3). - draw_rectangle(0,0,w - 1,h - 1,gray). - draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). - draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black); - if (logo._data) canvas.draw_image(lx,ly,logo); - - unsigned int xbuttons[6] = { 0 }; - cimglist_for(buttons,lll) { xbuttons[lll] = bx + (bw + 12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); } - - // Open window and enter events loop - CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false); - if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2, - (CImgDisplay::screen_height() - disp.height())/2); - bool stop_flag = false, refresh = false; - int oselected = -1, oclicked = -1, selected = -1, clicked = -1; - while (!disp.is_closed() && !stop_flag) { - if (refresh) { - if (clicked>=0) - CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); - else { - if (selected>=0) - CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); - else canvas.display(disp); - } - refresh = false; - } - disp.wait(15); - if (disp.is_resized()) disp.resize(disp,false); - - if (disp.button()&1) { - oclicked = clicked; - clicked = -1; - cimglist_for(buttons,l) - if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) && - disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) { - clicked = selected = l; - refresh = true; - } - if (clicked!=oclicked) refresh = true; - } else if (clicked>=0) stop_flag = true; - - if (disp.key()) { - oselected = selected; - switch (disp.key()) { - case cimg::keyESC : selected = -1; stop_flag = true; break; - case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break; - case cimg::keyTAB : - case cimg::keyARROWRIGHT : - case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break; - case cimg::keyARROWLEFT : - case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break; - } - disp.set_key(); - if (selected!=oselected) refresh = true; - } - } - if (!disp) selected = -1; - return selected; -#endif - } - - //! Display a simple dialog box, and wait for the user's response \specialization. - inline int dialog(const char *const title, const char *const msg, - const char *const button1_label, const char *const button2_label, const char *const button3_label, - const char *const button4_label, const char *const button5_label, const char *const button6_label, - const bool is_centered) { - return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, - CImg<unsigned char>::_logo40x38(),is_centered); - } - - //! Evaluate math expression. - /** - \param expression C-string describing the formula to evaluate. - \param x Value of the pre-defined variable \c x. - \param y Value of the pre-defined variable \c y. - \param z Value of the pre-defined variable \c z. - \param c Value of the pre-defined variable \c c. - \return Result of the formula evaluation. - \note Set \c expression to \c 0 to keep evaluating the last specified \c expression. - \par Example - \code - const double - res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2), // will return '1' - res2 = cimg::eval(0,1,1); // will return '1' too - \endcode - **/ - inline double eval(const char *const expression, const double x, const double y, const double z, const double c) { - static const CImg<float> empty; - return empty.eval(expression,x,y,z,c); - } - - template<typename t> - inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) { - static const CImg<float> empty; - return empty.eval(expression,xyzc); - } - - // End of cimg:: namespace -} - - // End of cimg_library:: namespace -} - -//! Short alias name. -namespace cil = cimg_library_suffixed; - -#ifdef _cimg_redefine_False -#define False 0 -#endif -#ifdef _cimg_redefine_True -#define True 1 -#endif -#ifdef _cimg_redefine_min -#define min(a,b) (((a)<(b))?(a):(b)) -#endif -#ifdef _cimg_redefine_max -#define max(a,b) (((a)>(b))?(a):(b)) -#endif -#ifdef _cimg_redefine_PI -#define PI 3.141592653589793238462643383 -#endif -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif -// Local Variables: -// mode: c++ -// End: diff --git a/src/tools/cimg/Licence_CeCILL-C_V1-en.txt b/src/tools/cimg/Licence_CeCILL-C_V1-en.txt deleted file mode 100644 index 2e9ffba..0000000 --- a/src/tools/cimg/Licence_CeCILL-C_V1-en.txt +++ /dev/null @@ -1,508 +0,0 @@ - - CeCILL-C FREE SOFTWARE LICENSE AGREEMENT - - - Notice - -This Agreement is a Free Software license agreement that is the result -of discussions between its authors in order to ensure compliance with -the two main principles guiding its drafting: - - * firstly, compliance with the principles governing the distribution - of Free Software: access to source code, broad rights granted to - users, - * secondly, the election of a governing law, French law, with which - it is conformant, both as regards the law of torts and - intellectual property law, and the protection that it offers to - both authors and holders of the economic rights over software. - -The authors of the CeCILL-C (for Ce[a] C[nrs] I[nria] L[logiciel] L[ibre]) -license are: - -Commissariat à l'Energie Atomique - CEA, a public scientific, technical -and industrial research establishment, having its principal place of -business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. - -Centre National de la Recherche Scientifique - CNRS, a public scientific -and technological establishment, having its principal place of business -at 3 rue Michel-Ange, 75794 Paris cedex 16, France. - -Institut National de Recherche en Informatique et en Automatique - -INRIA, a public scientific and technological establishment, having its -principal place of business at Domaine de Voluceau, Rocquencourt, BP -105, 78153 Le Chesnay cedex, France. - - - Preamble - -The purpose of this Free Software license agreement is to grant users the -right to modify and re-use the software governed by this license. - -The exercising of this right is conditional on the obligation to make -available to the community the modifications made to the source code of the -software so as to contribute to its evolution. - -In consideration of access to the source code and the rights to copy, -modify and redistribute granted by the license, users are provided only -with a limited warranty and the software's author, the holder of the -economic rights, and the successive licensors only have limited liability. - -In this respect, the risks associated with loading, using, modifying -and/or developing or reproducing the software by the user are brought to -the user's attention, given its Free Software status, which may make it -complicated to use, with the result that its use is reserved for -developers and experienced professionals having in-depth computer -knowledge. Users are therefore encouraged to load and test the suitability -of the software as regards their requirements in conditions enabling the -security of their systems and/or data to be ensured and, more generally, to -use and operate it in the same conditions of security. This Agreement may be -freely reproduced and published, provided it is not altered, and that no -provisions are either added or removed herefrom. - -This Agreement may apply to any or all software for which the holder of -the economic rights decides to submit the use thereof to its provisions. - - - Article 1 - DEFINITIONS - -For the purpose of this Agreement, when the following expressions -commence with a capital letter, they shall have the following meaning: - -Agreement: means this license agreement, and its possible subsequent -versions and annexes. - -Software: means the software in its Object Code and/or Source Code form -and, where applicable, its documentation, "as is" when the Licensee -accepts the Agreement. - -Initial Software: means the Software in its Source Code and possibly its -Object Code form and, where applicable, its documentation, "as is" when -it is first distributed under the terms and conditions of the Agreement. - -Modified Software: means the Software modified by at least one Integrated -Contribution. - -Source Code: means all the Software's instructions and program lines to -which access is required so as to modify the Software. - -Object Code: means the binary files originating from the compilation of -the Source Code. - -Holder: means the holder(s) of the economic rights over the Initial -Software. - -Licensee: means the Software user(s) having accepted the Agreement. - -Contributor: means a Licensee having made at least one Integrated -Contribution. - -Licensor: means the Holder, or any other individual or legal entity, who -distributes the Software under the Agreement. - -Integrated Contribution: means any or all modifications, corrections, -translations, adaptations and/or new functions integrated into the Source -Code by any or all Contributors. - -Related Module: means a set of sources files including their documentation -that, without modification to the Source Code, enables supplementary -functions or services in addition to those offered by the Software. - -Derivative Software: means any combination of the Software, modified or not, -and of a Related Module. - -Parties: mean both the Licensee and the Licensor. - -These expressions may be used both in singular and plural form. - - - Article 2 - PURPOSE - -The purpose of the Agreement is the grant by the Licensor to the -Licensee of a non-exclusive, transferable and worldwide license for the -Software as set forth in Article 5 hereinafter for the whole term of the -protection granted by the rights over said Software. - - - Article 3 - ACCEPTANCE - -3.1 The Licensee shall be deemed as having accepted the terms and -conditions of this Agreement upon the occurrence of the first of the -following events: - - * (i) loading the Software by any or all means, notably, by - downloading from a remote server, or by loading from a physical - medium; - * (ii) the first time the Licensee exercises any of the rights - granted hereunder. - -3.2 One copy of the Agreement, containing a notice relating to the -characteristics of the Software, to the limited warranty, and to the -fact that its use is restricted to experienced users has been provided -to the Licensee prior to its acceptance as set forth in Article 3.1 -hereinabove, and the Licensee hereby acknowledges that it has read and -understood it. - - - Article 4 - EFFECTIVE DATE AND TERM - - - 4.1 EFFECTIVE DATE - -The Agreement shall become effective on the date when it is accepted by -the Licensee as set forth in Article 3.1. - - - 4.2 TERM - -The Agreement shall remain in force for the entire legal term of -protection of the economic rights over the Software. - - - Article 5 - SCOPE OF RIGHTS GRANTED - -The Licensor hereby grants to the Licensee, who accepts, the following -rights over the Software for any or all use, and for the term of the -Agreement, on the basis of the terms and conditions set forth hereinafter. - -Besides, if the Licensor owns or comes to own one or more patents -protecting all or part of the functions of the Software or of its -components, the Licensor undertakes not to enforce the rights granted by -these patents against successive Licensees using, exploiting or -modifying the Software. If these patents are transferred, the Licensor -undertakes to have the transferees subscribe to the obligations set -forth in this paragraph. - - - 5.1 RIGHT OF USE - -The Licensee is authorized to use the Software, without any limitation -as to its fields of application, with it being hereinafter specified -that this comprises: - - 1. permanent or temporary reproduction of all or part of the Software - by any or all means and in any or all form. - 2. loading, displaying, running, or storing the Software on any or - all medium. - 3. entitlement to observe, study or test its operation so as to - determine the ideas and principles behind any or all constituent - elements of said Software. This shall apply when the Licensee - carries out any or all loading, displaying, running, transmission - or storage operation as regards the Software, that it is entitled - to carry out hereunder. - - - 5.2 RIGHT OF MODIFICATION - -The right of modification includes the right to translate, adapt, arrange, -or make any or all modifications to the Software, and the right to -reproduce the resulting Software. It includes, in particular, the right -to create a Derivative Software. - -The Licensee is authorized to make any or all modification to the -Software provided that it includes an explicit notice that it is the -author of said modification and indicates the date of the creation thereof. - - - 5.3 RIGHT OF DISTRIBUTION - -In particular, the right of distribution includes the right to publish, -transmit and communicate the Software to the general public on any or -all medium, and by any or all means, and the right to market, either in -consideration of a fee, or free of charge, one or more copies of the -Software by any means. - -The Licensee is further authorized to distribute copies of the modified -or unmodified Software to third parties according to the terms and -conditions set forth hereinafter. - - - 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION - -The Licensee is authorized to distribute true copies of the Software in -Source Code or Object Code form, provided that said distribution -complies with all the provisions of the Agreement and is accompanied by: - - 1. a copy of the Agreement, - - 2. a notice relating to the limitation of both the Licensor's - warranty and liability as set forth in Articles 8 and 9, - -and that, in the event that only the Object Code of the Software is -redistributed, the Licensee allows effective access to the full Source Code -of the Software at a minimum during the entire period of its distribution -of the Software, it being understood that the additional cost of acquiring -the Source Code shall not exceed the cost of transferring the data. - - - 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE - -When the Licensee makes an Integrated Contribution to the Software, the terms -and conditions for the distribution of the resulting Modified Software become -subject to all the provisions of this Agreement. - -The Licensee is authorized to distribute the Modified Software, in source -code or object code form, provided that said distribution complies with all -the provisions of the Agreement and is accompanied by: - - 1. a copy of the Agreement, - 2. a notice relating to the limitation of both the Licensor's warranty and - liability as set forth in Articles 8 and 9, - -and that, in the event that only the object code of the Modified Software is -redistributed, the Licensee allows effective access to the full source code -of the Modified Software at a minimum during the entire period of its -distribution of the Modified Software, it being understood that the -additional cost of acquiring the source code shall not exceed the cost of -transferring the data. - - 5.3.3 DISTRIBUTION OF DERIVATIVE SOFTWARE - -When the Licensee creates Derivative Software, this Derivative Software may -be distributed under a license agreement other than this Agreement, subject -to compliance with the requirement to include a notice concerning the rights -over the Software as defined in Article 6.4. In the event the creation of the -Derivative Software required modification of the Source Code, the Licensee -undertakes that: - - 1. the resulting Modified Software will be governed by this Agreement, - 2. the Integrated Contributions in the resulting Modified Software will be - clearly identified and documented, - 3. the Licensee will allow effective access to the source code of the - Modified Software, at a minimum during the entire period of - distribution of the Derivative Software, such that such modifications - may be carried over in a subsequent version of the Software; it being - understood that the additional cost of purchasing the source code of - the Modified Software shall not exceed the cost of transferring the - data. - - - 5.3.4 COMPATIBILITY WITH THE CeCILL LICENSE - -When a Modified Software contains an Integrated Contribution subject to the -CeCill license agreement, or when a Derivative Software contains a Related -Module subject to the CeCill license agreement, the provisions set forth in -the third item of Article 6.4 are optional. - - - Article 6 - INTELLECTUAL PROPERTY - - - 6.1 OVER THE INITIAL SOFTWARE - -The Holder owns the economic rights over the Initial Software. Any or -all use of the Initial Software is subject to compliance with the terms -and conditions under which the Holder has elected to distribute its work -and no one shall be entitled to modify the terms and conditions for the -distribution of said Initial Software. - -The Holder undertakes that the Initial Software will remain ruled at -least by the current license, for the duration set forth in Article 4.2. - - - 6.2 OVER THE INTEGRATED CONTRIBUTIONS - -A Licensee who develops an Integrated Contribution is the owner of the -intellectual property rights over this Contribution as defined by -applicable law. - - - 6.3 OVER THE RELATED MODULES - -A Licensee who develops an Related Module is the owner of the -intellectual property rights over this Related Module as defined by -applicable law and is free to choose the type of agreement that shall -govern its distribution under the conditions defined in Article 5.3.3. - - - 6.4 NOTICE OF RIGHTS - -The Licensee expressly undertakes: - - 1. not to remove, or modify, in any manner, the intellectual property - notices attached to the Software; - 2. to reproduce said notices, in an identical manner, in the copies - of the Software modified or not; - 3. to ensure that use of the Software, its intellectual property - notices and the fact that it is governed by the Agreement is - indicated in a text that is easily accessible, specifically from - the interface of any Derivative Software. - -The Licensee undertakes not to directly or indirectly infringe the -intellectual property rights of the Holder and/or Contributors on the -Software and to take, where applicable, vis-à-vis its staff, any and all -measures required to ensure respect of said intellectual property rights -of the Holder and/or Contributors. - - - Article 7 - RELATED SERVICES - -7.1 Under no circumstances shall the Agreement oblige the Licensor to -provide technical assistance or maintenance services for the Software. - -However, the Licensor is entitled to offer this type of services. The -terms and conditions of such technical assistance, and/or such -maintenance, shall be set forth in a separate instrument. Only the -Licensor offering said maintenance and/or technical assistance services -shall incur liability therefor. - -7.2 Similarly, any Licensor is entitled to offer to its licensees, under -its sole responsibility, a warranty, that shall only be binding upon -itself, for the redistribution of the Software and/or the Modified -Software, under terms and conditions that it is free to decide. Said -warranty, and the financial terms and conditions of its application, -shall be subject of a separate instrument executed between the Licensor -and the Licensee. - - - Article 8 - LIABILITY - -8.1 Subject to the provisions of Article 8.2, the Licensee shall be -entitled to claim compensation for any direct loss it may have suffered -from the Software as a result of a fault on the part of the relevant -Licensor, subject to providing evidence thereof. - -8.2 The Licensor's liability is limited to the commitments made under -this Agreement and shall not be incurred as a result of in particular: -(i) loss due the Licensee's total or partial failure to fulfill its -obligations, (ii) direct or consequential loss that is suffered by the -Licensee due to the use or performance of the Software, and (iii) more -generally, any consequential loss. In particular the Parties expressly -agree that any or all pecuniary or business loss (i.e. loss of data, -loss of profits, operating loss, loss of customers or orders, -opportunity cost, any disturbance to business activities) or any or all -legal proceedings instituted against the Licensee by a third party, -shall constitute consequential loss and shall not provide entitlement to -any or all compensation from the Licensor. - - - Article 9 - WARRANTY - -9.1 The Licensee acknowledges that the scientific and technical -state-of-the-art when the Software was distributed did not enable all -possible uses to be tested and verified, nor for the presence of -possible defects to be detected. In this respect, the Licensee's -attention has been drawn to the risks associated with loading, using, -modifying and/or developing and reproducing the Software which are -reserved for experienced users. - -The Licensee shall be responsible for verifying, by any or all means, -the suitability of the product for its requirements, its good working order, -and for ensuring that it shall not cause damage to either persons or -properties. - -9.2 The Licensor hereby represents, in good faith, that it is entitled -to grant all the rights over the Software (including in particular the -rights set forth in Article 5). - -9.3 The Licensee acknowledges that the Software is supplied "as is" by -the Licensor without any other express or tacit warranty, other than -that provided for in Article 9.2 and, in particular, without any warranty -as to its commercial value, its secured, safe, innovative or relevant -nature. - -Specifically, the Licensor does not warrant that the Software is free -from any error, that it will operate without interruption, that it will -be compatible with the Licensee's own equipment and software -configuration, nor that it will meet the Licensee's requirements. - -9.4 The Licensor does not either expressly or tacitly warrant that the -Software does not infringe any third party intellectual property right -relating to a patent, software or any other property right. Therefore, -the Licensor disclaims any and all liability towards the Licensee -arising out of any or all proceedings for infringement that may be -instituted in respect of the use, modification and redistribution of the -Software. Nevertheless, should such proceedings be instituted against -the Licensee, the Licensor shall provide it with technical and legal -assistance for its defense. Such technical and legal assistance shall be -decided on a case-by-case basis between the relevant Licensor and the -Licensee pursuant to a memorandum of understanding. The Licensor -disclaims any and all liability as regards the Licensee's use of the -name of the Software. No warranty is given as regards the existence of -prior rights over the name of the Software or as regards the existence -of a trademark. - - - Article 10 - TERMINATION - -10.1 In the event of a breach by the Licensee of its obligations -hereunder, the Licensor may automatically terminate this Agreement -thirty (30) days after notice has been sent to the Licensee and has -remained ineffective. - -10.2 A Licensee whose Agreement is terminated shall no longer be -authorized to use, modify or distribute the Software. However, any -licenses that it may have granted prior to termination of the Agreement -shall remain valid subject to their having been granted in compliance -with the terms and conditions hereof. - - - Article 11 - MISCELLANEOUS - - - 11.1 EXCUSABLE EVENTS - -Neither Party shall be liable for any or all delay, or failure to -perform the Agreement, that may be attributable to an event of force -majeure, an act of God or an outside cause, such as defective -functioning or interruptions of the electricity or telecommunications -networks, network paralysis following a virus attack, intervention by -government authorities, natural disasters, water damage, earthquakes, -fire, explosions, strikes and labor unrest, war, etc. - -11.2 Any failure by either Party, on one or more occasions, to invoke -one or more of the provisions hereof, shall under no circumstances be -interpreted as being a waiver by the interested Party of its right to -invoke said provision(s) subsequently. - -11.3 The Agreement cancels and replaces any or all previous agreements, -whether written or oral, between the Parties and having the same -purpose, and constitutes the entirety of the agreement between said -Parties concerning said purpose. No supplement or modification to the -terms and conditions hereof shall be effective as between the Parties -unless it is made in writing and signed by their duly authorized -representatives. - -11.4 In the event that one or more of the provisions hereof were to -conflict with a current or future applicable act or legislative text, -said act or legislative text shall prevail, and the Parties shall make -the necessary amendments so as to comply with said act or legislative -text. All other provisions shall remain effective. Similarly, invalidity -of a provision of the Agreement, for any reason whatsoever, shall not -cause the Agreement as a whole to be invalid. - - - 11.5 LANGUAGE - -The Agreement is drafted in both French and English and both versions -are deemed authentic. - - - Article 12 - NEW VERSIONS OF THE AGREEMENT - -12.1 Any person is authorized to duplicate and distribute copies of this -Agreement. - -12.2 So as to ensure coherence, the wording of this Agreement is -protected and may only be modified by the authors of the License, who -reserve the right to periodically publish updates or new versions of the -Agreement, each with a separate number. These subsequent versions may -address new issues encountered by Free Software. - -12.3 Any Software distributed under a given version of the Agreement -may only be subsequently distributed under the same version of the -Agreement or a subsequent version. - - - Article 13 - GOVERNING LAW AND JURISDICTION - -13.1 The Agreement is governed by French law. The Parties agree to -endeavor to seek an amicable solution to any disagreements or disputes -that may arise during the performance of the Agreement. - -13.2 Failing an amicable solution within two (2) months as from their -occurrence, and unless emergency proceedings are necessary, the -disagreements or disputes shall be referred to the Paris Courts having -jurisdiction, by the more diligent Party. - - -Version 1.0 dated 2006-07-12. diff --git a/src/tools/sstv-encode.cpp b/src/tools/sstv-encode.cpp index 3919968..51b5a33 100644 --- a/src/tools/sstv-encode.cpp +++ b/src/tools/sstv-encode.cpp @@ -7,11 +7,11 @@ #include <iostream> #include <malloc.h> -#include <sndfile.h> #include <glog/logging.h> #include <gflags/gflags.h> -#include "cimg/CImg.h" +#include <Magick++.h> +#include <sndfile.h> extern "C" { #include <libsstv.h> @@ -50,12 +50,62 @@ int main(int argc, char **argv) /* TODO: parse SSTV mode */ sstv_mode_t mode = SSTV_MODE_PD120; - /* load image from file (TODO: perform normalization from source to desired properties) */ + /* get image properties for chosen mode */ + size_t width, height; + sstv_image_format_t format; + if (sstv_get_mode_image_props(mode, &width, &height, &format) != SSTV_OK) { + LOG(FATAL) << "sstv_get_mode_image_props() failed"; + } + + /* load image from file */ LOG(INFO) << "Loading image from " << FLAGS_input; - cimg_library::CImg<unsigned char> input_image = (cimg_library::CImg<>(FLAGS_input.c_str())).RGBtoYCbCr(); + + Magick::Image image; + std::string map = ""; + uint8_t *image_buffer = NULL; + + try { + /* load from file */ + image.read(FLAGS_input); + + /* resize */ + LOG(INFO) << "Resizing to " << width << "x"<<height; + Magick::Geometry nsize(width, height); + nsize.aspect(true); + image.scale(nsize); + + /* format */ + //format = SSTV_FORMAT_Y; + switch (format) { + case SSTV_FORMAT_Y: + map = "R"; + image.magick("Y"); + break; + + case SSTV_FORMAT_YCBCR: + map = "RGB"; + image.colorSpace(Magick::YCbCrColorspace); + break; + + case SSTV_FORMAT_RGB: + map = "RGB"; + image.colorSpace(Magick::RGBColorspace); + break; + + default: + LOG(FATAL) << "Unknown pixel format"; + break; + } + } catch (int e) { + LOG(FATAL) << "Magick++ failed"; + } + + /* get raw */ + Magick::PixelData blob(image, map, Magick::CharPixel); + image_buffer = (uint8_t *)blob.data(); sstv_image_t sstv_image; - if (sstv_pack_image(&sstv_image, input_image.width(), input_image.height(), SSTV_FORMAT_YCBCR, input_image.data()) != SSTV_OK) { + if (sstv_pack_image(&sstv_image, width, height, format, image_buffer) != SSTV_OK) { LOG(FATAL) << "sstv_pack_image() failed"; } diff --git a/test/test-image-2.bmp b/test/test-image-2.bmp Binary files differnew file mode 100644 index 0000000..4165f0a --- /dev/null +++ b/test/test-image-2.bmp diff --git a/test/test-image.bmp b/test/test-image.bmp Binary files differindex d8e6d80..90a240d 100644 --- a/test/test-image.bmp +++ b/test/test-image.bmp |
