diff --git a/CImg.h b/CImg.h index fe0a1d0..c53a7fc 100644 --- a/CImg.h +++ b/CImg.h @@ -5,12 +5,12 @@ # # Description : The C++ Template Image Processing Toolkit. # This file is the main component of the CImg Library project. - # ( http://cimg.sourceforge.net ) + # ( http://cimg.eu ) # # Project manager : David Tschumperle. - # ( http://www.greyc.ensicaen.fr/~dtschump/ ) + # ( http://tschumperle.users.greyc.fr/ ) # - # The complete list of contributors is available in file 'README.txt' + # 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 @@ -28,7 +28,7 @@ # 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". + # 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 @@ -52,43 +52,47 @@ # */ -// Define version number of the library file. +// Set version number of the library. #ifndef cimg_version -#define cimg_version 149 +#define cimg_version 203 /*----------------------------------------------------------- # - # Test and auto-set CImg configuration variables + # Test and possibly auto-set CImg configuration variables # and include required headers. # - # If you find that default configuration variables are - # not adapted to your case, you can override their values + # 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 required standard C++ headers. +// Include standard C++ headers. +// This is the minimal set of required headers to make CImg-based codes compile. #include #include #include #include #include +#include +#include #include #include +#include -// Operating system configuration. +// Detect/configure OS variables. // -// Define 'cimg_OS' to : '0' for an unknown OS (will try to minize library dependancies). -// '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). -// '2' for Microsoft Windows. -// (autodetection is done by default). +// 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(__FreeBSD__) || defined (__DragonFly__) \ || defined(sgi) || defined(__sgi) \ || defined(__MACOSX__) || defined(__APPLE__) \ || defined(__CYGWIN__) @@ -100,49 +104,149 @@ #define cimg_OS 0 #endif #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2) -#error CImg Library : Configuration variable 'cimg_OS' is badly defined. -#error (valid values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows'). +#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 Microsoft VC++ compilers. +// 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) #define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_WARNINGS 1 #define _CRT_NONSTDC_NO_DEPRECATE 1 #endif -// Include OS-specific headers for system management. +// 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 +#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 #include +#include #include +#include +#include #elif cimg_OS==2 +#ifndef std_fopen +#define std_fopen cimg::win_fopen +#endif #ifndef NOMINMAX #define NOMINMAX #endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif #include #ifndef _WIN32_IE #define _WIN32_IE 0x0400 #endif #include +#include #include -#define cimg_snprintf _snprintf -#define cimg_vsnprintf _vsnprintf -#endif -#ifndef cimg_snprintf -#include -#define cimg_snprintf snprintf -#define cimg_vsnprintf vsnprintf #endif -// Filename separator configuration. +// 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 +#include +#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) +#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. // -// Default separator is '/' for Unix-based OS, and '\' or Windows. +// 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 '\\' @@ -151,46 +255,108 @@ #endif #endif -// Output messages verbosity configuration. +// Configure verbosity of output messages. // -// Define 'cimg_verbosity' to : '0' to hide library messages (quiet mode). -// '1' to print library messages on the console. -// '2' to display library messages on a 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_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 (require VT100-compatible terminal). +// 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 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 -// Display framework configuration. +// Configure display framework. // -// Define 'cimg_display' to : '0' to disable display capabilities. -// '1' to use X-Window framework (X11). -// '2' to use Microsoft GDI32 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 -#if defined(__MACOSX__) || defined(__APPLE__) #define cimg_display 1 -#else -#define cimg_display 1 -#endif #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 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 +#define cimg_abort_init bool cimg_abort_go = true; cimg::unused(cimg_abort_go) +#endif +#ifndef cimg_abort_try +#define cimg_abort_try if (cimg_abort_go) try +#endif +#ifndef cimg_abort_catch +#define cimg_abort_catch() catch (CImgAbortException&) { cimg_pragma(omp atomic) cimg_abort_go&=false; } +#endif +#ifdef cimg_abort_test2 +#ifndef cimg_abort_try2 +#define cimg_abort_try2 cimg_abort_try +#endif +#ifndef cimg_abort_catch2 +#define cimg_abort_catch2() cimg_abort_catch() +#endif +#ifndef cimg_abort_catch_fill +#define cimg_abort_catch_fill() \ + catch (CImgException& e) { cimg_pragma(omp critical) CImg::string(e._message).move_to(is_error); \ + cimg_pragma(omp atomic) cimg_abort_go&=false; } +#endif +#endif + +#endif + +#ifndef cimg_abort_test +#define cimg_abort_test() +#endif +#ifndef cimg_abort_test2 +#define cimg_abort_test2() +#endif +#ifndef cimg_abort_init +#define cimg_abort_init +#endif +#ifndef cimg_abort_try +#define cimg_abort_try +#endif +#ifndef cimg_abort_catch +#define cimg_abort_catch() +#endif +#ifndef cimg_abort_try2 +#define cimg_abort_try2 +#endif +#ifndef cimg_abort_catch2 +#define cimg_abort_catch2() +#endif +#ifndef cimg_abort_catch_fill +#define cimg_abort_catch_fill() +#endif +#ifndef std_fopen +#define std_fopen std::fopen +#endif + // Include display-specific headers. #if cimg_display==1 #include @@ -210,54 +376,61 @@ #define cimg_appname "CImg" #endif -// OpenMP configuration. +// Configure OpenMP support. // (http://www.openmp.org) // // Define 'cimg_use_openmp' to enable OpenMP support. // -// OpenMP directives can be used in few CImg functions to get -// advantages of multi-core CPUs. Using OpenMP is not mandatory. +// OpenMP directives may be used in a (very) few CImg functions to get +// advantages of multi-core CPUs. #ifdef cimg_use_openmp -#include "omp.h" -#define _cimg_static +#include +#define cimg_pragma_openmp(p) cimg_pragma(omp p) #else -#define _cimg_static static +#define cimg_pragma_openmp(p) #endif -// OpenCV configuration +// Configure OpenCV support. // (http://opencv.willowgarage.com/wiki/) // // Define 'cimg_use_opencv' to enable OpenCV support. // -// OpenCV can be used to retrieve images from cameras -// (with function 'CImg::load_camera()'. -// Using OpenCV is not mandatory. +// OpenCV library may be used to access images from cameras +// (see method 'CImg::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 #include "cv.h" #include "highgui.h" #endif -// LibPNG configuration. +// Configure LibPNG support. // (http://www.libpng.org) // // Define 'cimg_use_png' to enable LibPNG support. // -// LibPNG can be used in functions 'CImg::{load,save}_png()' -// to get a builtin support of PNG files. Using LibPNG is not mandatory. +// PNG library may be used to get a native support of '.png' files. +// (see methods 'CImg::{load,save}_png()'. #ifdef cimg_use_png extern "C" { #include "png.h" } #endif -// LibJPEG configuration. +// Configure LibJPEG support. // (http://en.wikipedia.org/wiki/Libjpeg) // // Define 'cimg_use_jpeg' to enable LibJPEG support. // -// LibJPEG can be used in functions 'CImg::{load,save}_jpeg()' -// to get a builtin support of JPEG files. Using LibJPEG is not mandatory. +// JPEG library may be used to get a native support of '.jpg' files. +// (see methods 'CImg::{load,save}_jpeg()'). #ifdef cimg_use_jpeg extern "C" { #include "jpeglib.h" @@ -265,115 +438,104 @@ extern "C" { } #endif -// LibTIFF configuration. +// Configure LibTIFF support. // (http://www.libtiff.org) // // Define 'cimg_use_tiff' to enable LibTIFF support. // -// LibTIFF can be used in functions 'CImg[List]::{load,save}_tiff()' -// to get a builtin support of TIFF files. Using LibTIFF is not mandatory. +// TIFF library may be used to get a native support of '.tif' files. +// (see methods 'CImg[List]::{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 -// LibMINC2 configuration. +// Configure LibMINC2 support. // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference) // // Define 'cimg_use_minc2' to enable LibMINC2 support. // -// LibMINC2 can be used in functions 'CImg::{load,save}_minc2()' -// to get a builtin support of MINC2 files. Using LibMINC2 is not mandatory. +// MINC2 library may be used to get a native support of '.mnc' files. +// (see methods 'CImg::{load,save}_minc2()'). #ifdef cimg_use_minc2 -extern "C" { -#include "minc2.h" -} +#include "minc_io_simple_volume.h" +#include "minc_1_simple.h" +#include "minc_1_simple_rw.h" #endif -// FFMPEG Avcodec and Avformat libraries configuration. -// (http://www.ffmpeg.org) -// -// Define 'cimg_use_ffmpeg' to enable FFMPEG lib support. -// -// Avcodec and Avformat libraries can be used in functions -// 'CImg[List]::load_ffmpeg()' to get a builtin -// support of various image sequences files. -// Using FFMPEG libraries is not mandatory. -#ifdef cimg_use_ffmpeg -#if (defined(_STDINT_H) || defined(_STDINT_H_)) && !defined(UINT64_C) -#warning "__STDC_CONSTANT_MACROS has to be defined before including , this file will probably not compile." -#endif -#ifndef __STDC_CONSTANT_MACROS -#define __STDC_CONSTANT_MACROS // ...or stdint.h doesn't define UINT64_C, needed for libavutil -#endif -extern "C" { -#include "avformat.h" -#include "avcodec.h" -#include "swscale.h" -} -#endif - -// Zlib configuration +// Configure Zlib support. // (http://www.zlib.net) // // Define 'cimg_use_zlib' to enable Zlib support. // -// Zlib can be used in functions 'CImg[List]::{load,save}_cimg()' -// to allow compressed data in '.cimg' files. Using Zlib is not mandatory. +// Zlib library may be used to allow compressed data in '.cimgz' files +// (see methods 'CImg[List]::{load,save}_cimg()'). #ifdef cimg_use_zlib extern "C" { #include "zlib.h" } #endif -// Magick++ configuration. +// 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 can be used in functions 'CImg::{load,save}()' -// to get a builtin support of various image formats (PNG,JPEG,TIFF,...). -// Using Magick++ is not mandatory. +// Magick++ library may be used to get a native support of various image file formats. +// (see methods 'CImg::{load,save}()'). #ifdef cimg_use_magick #include "Magick++.h" #endif -// FFTW3 configuration. +// Configure FFTW3 support. // (http://www.fftw.org) // // Define 'cimg_use_fftw3' to enable libFFTW3 support. // -// FFTW3 library can be used in functions 'CImg[List]::FFT()' to -// efficiently compute the Fast Fourier Transform of image data. +// 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]::FFT()'). #ifdef cimg_use_fftw3 extern "C" { #include "fftw3.h" } #endif -// Board configuration +// Configure LibBoard support. // (http://libboard.sourceforge.net/) // // Define 'cimg_use_board' to enable Board support. // -// Board library can be used in functions 'CImg::draw_object3d()' -// to draw objects 3d in vector-graphics canvas that can be saved -// as .PS or .SVG files afterwards. +// 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::draw_object3d()'). #ifdef cimg_use_board -#ifdef None -#undef None -#define _cimg_redefine_None -#endif #include "Board.h" #endif -// OpenEXR configuration +// Configure OpenEXR support. // (http://www.openexr.com/) // // Define 'cimg_use_openexr' to enable OpenEXR support. // -// OpenEXR can be used to read/write .exr file formats. +// OpenEXR library may be used to get a native support of '.exr' files. +// (see methods 'CImg::{load,save}_exr()'). #ifdef cimg_use_openexr #include "ImfRgbaFile.h" #include "ImfInputFile.h" @@ -382,14 +544,26 @@ extern "C" { #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 can be used in various CImg functions dealing with -// matrix computation and algorithms (eigenvalues, inverse, ...). -// Using Lapack is not mandatory. +// 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*); @@ -400,17 +574,20 @@ extern "C" { 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 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 min(), max() and PI labels are defined and used in the cimg:: namespace. +// 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 the file. +// values at the end of this file. #ifdef min #undef min #define _cimg_redefine_min @@ -424,31 +601,33 @@ extern "C" { #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. # - # User macros are prefixed by 'cimg_' and can be used in your own code. - # They are particularly useful for option parsing, and image loops creation. + # 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. # ------------------------------------------------------------------------------*/ -// Define the program usage, and retrieve command line arguments. -#define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage,false) -#define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0) -#define cimg_option(name,defaut,usage) cimg_library::cimg::option(name,argc,argv,defaut,usage) -#define cimg_argument(pos) cimg_library::cimg::argument(pos,argc,argv) -#define cimg_argument1(pos,s0) cimg_library::cimg::argument(pos,argc,argv,1,s0) -#define cimg_argument2(pos,s0,s1) cimg_library::cimg::argument(pos,argc,argv,2,s0,s1) -#define cimg_argument3(pos,s0,s1,s2) cimg_library::cimg::argument(pos,argc,argv,3,s0,s1,s2) -#define cimg_argument4(pos,s0,s1,s2,s3) cimg_library::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3) -#define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4) -#define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5) -#define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6) -#define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7) -#define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8) +// 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) -// Define and manipulate local neighborhoods. +// 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]; \ @@ -516,115 +695,143 @@ extern "C" { 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) + 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) + 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) + 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[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) + 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) + 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); + 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) + 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) + 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) + 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) -// Define various image loops. +// Macros to perform various image loops. // -// These macros generally avoid the use of iterators, but you are not forced to used them ! -#define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; ) -#define cimg_foroff(img,off) for (unsigned int off = 0, _max##off = (unsigned int)(img).size(); off<_max##off; ++off) +// 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_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) #define cimg_forX(img,x) cimg_for1((img)._width,x) @@ -643,8 +850,25 @@ extern "C" { #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) + 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) @@ -659,32 +883,35 @@ extern "C" { #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_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) + 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)) + 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)) + 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)) + 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) @@ -695,29 +922,36 @@ extern "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_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_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) + 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) + --_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)), \ + _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; \ @@ -726,7 +960,7 @@ extern "C" { (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; \ + 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) @@ -746,7 +980,7 @@ extern "C" { #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; \ + _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) @@ -762,11 +996,12 @@ extern "C" { #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_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 = 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) @@ -786,8 +1021,8 @@ extern "C" { #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; \ + _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) @@ -803,11 +1038,12 @@ extern "C" { #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_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; \ + 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) @@ -827,9 +1063,9 @@ extern "C" { #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; \ + _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) @@ -845,12 +1081,13 @@ extern "C" { #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_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; \ + _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) @@ -870,10 +1107,10 @@ extern "C" { #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; \ + _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) @@ -889,13 +1126,14 @@ extern "C" { #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_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; \ + _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) @@ -915,12 +1153,13 @@ extern "C" { #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 = 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) @@ -935,13 +1174,14 @@ extern "C" { #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_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; \ + _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) @@ -961,13 +1201,14 @@ extern "C" { #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 = 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) @@ -982,14 +1223,15 @@ extern "C" { #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_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; \ + _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) @@ -1010,13 +1252,13 @@ extern "C" { #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; \ + _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) @@ -1033,14 +1275,15 @@ extern "C" { #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_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; \ + _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) @@ -1061,14 +1304,14 @@ extern "C" { #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; \ + _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) @@ -1085,14 +1328,15 @@ extern "C" { #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_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); \ + 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)) || \ @@ -1106,7 +1350,7 @@ extern "C" { _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 + 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)) || \ @@ -1121,8 +1365,8 @@ extern "C" { _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); \ + (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)), \ @@ -1135,7 +1379,7 @@ extern "C" { #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, \ + _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)), \ @@ -1143,7 +1387,7 @@ extern "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 + 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)), \ @@ -1157,7 +1401,7 @@ extern "C" { #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, \ + _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)), \ @@ -1167,7 +1411,7 @@ extern "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); \ + 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)), \ @@ -1182,8 +1426,8 @@ extern "C" { #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, \ + _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)), \ @@ -1197,7 +1441,7 @@ extern "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 + 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)), \ @@ -1213,7 +1457,7 @@ extern "C" { #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, \ + _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)), \ @@ -1225,7 +1469,7 @@ extern "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); \ + 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)), \ @@ -1242,9 +1486,9 @@ extern "C" { #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, \ + _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)), \ @@ -1266,7 +1510,7 @@ extern "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 + 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)), \ @@ -1284,8 +1528,8 @@ extern "C" { #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, \ + _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)), \ @@ -1305,7 +1549,7 @@ extern "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); \ + 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)), \ @@ -1324,10 +1568,10 @@ extern "C" { #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, \ + _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)), \ @@ -1359,7 +1603,7 @@ extern "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 + 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)), \ @@ -1379,8 +1623,8 @@ extern "C" { #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, \ + _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)), \ @@ -1403,7 +1647,7 @@ extern "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); \ + 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)), \ @@ -1424,11 +1668,11 @@ extern "C" { #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, \ + _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)), \ @@ -1472,7 +1716,7 @@ extern "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 + 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)), \ @@ -1494,9 +1738,9 @@ extern "C" { #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, \ + _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)), \ @@ -1530,7 +1774,7 @@ extern "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); \ + 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)), \ @@ -1553,12 +1797,12 @@ extern "C" { #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, \ + _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)), \ @@ -1616,7 +1860,7 @@ extern "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 + 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)), \ @@ -1640,9 +1884,9 @@ extern "C" { #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, \ + _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)), \ @@ -1680,7 +1924,7 @@ extern "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); \ + 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)), \ @@ -1693,25 +1937,27 @@ extern "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], \ + 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, \ + _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)), \ @@ -1785,7 +2031,7 @@ extern "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 + 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)), \ @@ -1798,14 +2044,16 @@ extern "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], \ + 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) \ @@ -1815,7 +2063,7 @@ extern "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); \ + 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)), \ @@ -1832,7 +2080,7 @@ extern "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 + 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)), \ @@ -1855,7 +2103,7 @@ extern "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); \ + 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)), \ @@ -1874,7 +2122,7 @@ extern "C" { #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, \ + _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)), \ @@ -1894,7 +2142,7 @@ extern "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 + 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)), \ @@ -1913,11 +2161,13 @@ extern "C" { #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) + 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 -// Define macros used when exceptions are thrown. +// 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>::" @@ -1928,171 +2178,337 @@ extern "C" { /*------------------------------------------------ # # - # Definition of the cimg_library:: namespace + # Define cimg_library:: namespace # # -------------------------------------------------*/ -//! This namespace encompasses all classes and functions of the %CImg library. +//! Contains all classes and functions of the \CImg library. /** This namespace is defined to avoid functions and class names collisions - that could happen with the include of other C++ header files. + 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 + \CImg-based programs with \code #include "CImg.h" using namespace cimg_library; \endcode - to simplify the declaration of %CImg Library variables afterwards. + to simplify the declaration of \CImg Library objects afterwards. **/ -namespace cimg_library { +namespace cimg_library_suffixed { - // Declare the only four classes of the CImg Library. + // Declare the four classes of the CImg Library. template struct CImg; template struct CImgList; struct CImgDisplay; struct CImgException; - // (Pre)declare the cimg namespace. - // This is not the complete namespace declaration. It only contains some - // necessary stuffs to ensure a correct declaration order of classes and functions + // 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 - const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; - const char t_red[] = { 0x1b, '[', '4', ';', '3', '1', ';', '5', '9', 'm', 0 }; - const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; - const char t_purple[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; - const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; + 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 - const char t_normal[] = { 0 }; - const char *const t_red = cimg::t_normal, *const t_bold = cimg::t_normal, - *const t_purple = cimg::t_normal, *const t_green = cimg::t_normal; + 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(); - // Function used to avoid warning messages due to unused parameters. + //! Avoid warning messages due to unused parameters. Do nothing actually. template inline void unused(const T&, ...) {} - //! Get/set the current CImg exception mode. - /** - The way error messages are handled by CImg can be changed dynamically, using this function. - Possible values are : - - '0' to hide library messages (quiet mode). - - '1' to print library messages on the console. - - '2' to display library messages on a 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 !). - **/ + // [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) mode = value; + if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } return mode; } - inline unsigned int& exception_mode() { - return _exception_mode(0,false); + + // 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); + } + + //! 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 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; + } + + 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); + } + +#define cimg_openmp_if(cond) if (cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond))) + + // 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 v=0); + // 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); + } - /*---------------------------------------------- - # - # Definition of the CImgException structures - # - ----------------------------------------------*/ - //! Instances of this class are thrown when errors occur during a %CImg library function call. + /*--------------------------------------- + # + # Define the CImgException structures + # + --------------------------------------*/ + //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call. /** - \section ex1 Overview + \par Overview - CImgException is the base class of %CImg exceptions. - Exceptions are thrown by the %CImg Library when an error occured in a %CImg library function call. - CImgException is seldom thrown itself. Children classes that specify the kind of error encountered - are generally used instead. These sub-classes are : + 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 CImgInstanceException : Thrown when the instance associated to the called %CImg function is not - correctly defined. Generally, this exception is thrown when one tries to process \a empty images. The example - below will throw a \a CImgInstanceException. + - \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 img; // Construct an empty image. - img.blur(10); // Try to blur the image. + CImg 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 CImgArgumentException : Thrown when one of the arguments given to the called %CImg function is not correct. - Generally, this exception is thrown when arguments passed to the function are outside an admissible range of values. - The example below will throw a \a CImgArgumentException. + - \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 - CImg img(100,100,1,3); // Define a 100x100 color image with float pixels. - img = 0; // Try to fill pixels from the 0 pointer (invalid argument to operator=() ). + const CImg 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. - The example below will throw a \a CImgIOException. + - \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 - CImg img("file_doesnt_exist.jpg"); // Try to load a file that doesn't exist. + const CImg img("missing_file.jpg"); // Try to load a file that does not exist. \endcode - The parent class CImgException may be thrown itself when errors that cannot be classified in one of - the above type occur. It is recommended not to throw CImgExceptions yourself, since there are normally - reserved to %CImg Library functions. - \b CImgInstanceException, \b CImgArgumentException and \b CImgIOException are simple - subclasses of CImgException and are thus not detailled more in this reference documentation. + - \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()). - \section ex2 Exception handling + 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). - When an error occurs, the %CImg Library first displays the error in a modal window. - Then, it throws an instance of the corresponding exception class, generally leading the program to stop - (this is the default behavior). - You can bypass this default behavior by handling the exceptions yourself, - using a code block try { ... } catch () { ... }. - In this case, you can avoid the apparition of the modal window, by - defining the environment variable cimg_verbosity to 0 before including the %CImg header file. - The example below shows how to cleanly handle %CImg Library exceptions : + \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 try { ... } catch () { ... } bloc, as in the following example: \code - #define cimg_verbosity 0 // Disable modal window in CImg exceptions. #define "CImg.h" + using namespace cimg_library; int main() { + cimg::exception_mode(0); // Enable quiet exception mode. try { - ...; // Here, do what you want. + ... // 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! + } } - catch (CImgInstanceException &e) { - std::fprintf(stderr,"CImg Library Error : %s",e.what()); // Display your own error message - ... // Do what you want now. - } - } \endcode **/ struct CImgException : public std::exception { #define _cimg_exception_err(etype,disp_flag) \ - std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,sizeof(_message),format,ap); va_end(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::cimg::info(); \ - } + 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[16384]; - CImgException() { *_message = 0; } - CImgException(const char *const format, ...) { _cimg_exception_err("CImgException",true); } + 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 CImgInstanceException class is used to throw an exception related - // to a non suitable instance encountered in a library function call. - struct CImgInstanceException : public CImgException { - CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } + // 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 @@ -2101,18 +2517,24 @@ namespace cimg_library { CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",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 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 { @@ -2120,21 +2542,21 @@ namespace cimg_library { }; /*------------------------------------- - # - # Definition of the namespace 'cimg' - # - --------------------------------------*/ - //! Namespace that encompasses \a low-level functions and variables of the %CImg Library. + # + # 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 library for low-level processing. - Nevertheless, documented variables and functions of this namespace may be used safely in your own source code. - - \warning Never write using namespace cimg_library::cimg; in your source code, since a lot of functions of the - cimg:: namespace have prototypes similar to standard C functions that could defined in the global namespace ::. + 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 using namespace cimg_library::cimg; in your source code. Lot of functions in the + cimg:: namespace have the same names as standard C functions that may be defined in the global + namespace ::. **/ namespace cimg { - // Define the traits that will be used to determine the best data type to work with. + // Define traits that will be used to determine the best data type to work in CImg functions. // template struct type { static const char* string() { @@ -2145,151 +2567,306 @@ namespace cimg_library { "unknown128" }; return s[(sizeof(T)<17)?sizeof(T):0]; } - static unsigned int id() { return 0U; } static bool is_float() { return false; } - static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); } - static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); } + 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(const T val) { static const char *const s = "unknown"; 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 { static const char* string() { static const char *const s = "bool"; return s; } - static unsigned int id() { return 1U; } 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 { static const char* string() { static const char *const s = "unsigned char"; return s; } - static unsigned int id() { return 2U; } 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)~0U; } - static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + 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 { static const char* string() { static const char *const s = "char"; return s; } - static unsigned int id() { return 3U; } static bool is_float() { return false; } - static char min() { return (char)(-1L<<(8*sizeof(char)-1)); } - static char max() { return ~((char)(-1L<<(8*sizeof(char)-1))); } + 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 { + 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 { static const char* string() { static const char *const s = "signed char"; return s; } - static unsigned int id() { return 4U; } static bool is_float() { return false; } - static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); } - static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); } - static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; } + 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 unsigned int format(const signed char val) { return (int)val; } + static const char* format_s() { return "%d"; } + static int format(const signed char val) { return (int)val; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned short"; return s; } - static unsigned int id() { return 5U; } 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)~0U; } - static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } + 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 { static const char* string() { static const char *const s = "short"; return s; } - static unsigned int id() { return 6U; } static bool is_float() { return false; } - static short min() { return (short)(-1L<<(8*sizeof(short)-1)); } - static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); } + 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 { static const char* string() { static const char *const s = "unsigned int"; return s; } - static unsigned int id() { return 7U; } 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)~0U; } - static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } + 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 { static const char* string() { static const char *const s = "int"; return s; } - static unsigned int id() { return 8U; } static bool is_float() { return false; } - static int min() { return (int)(-1L<<(8*sizeof(int)-1)); } - static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); } + 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 { - static const char* string() { static const char *const s = "unsigned long"; return s; } - static unsigned int id() { return 9U; } + template<> struct type { + static const char* string() { static const char *const s = "unsigned int64"; return s; } static bool is_float() { return false; } - static unsigned long min() { return 0; } - static unsigned long max() { return (unsigned long)~0UL; } - static unsigned long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned long)val; } - static const char* format() { return "%lu"; } - static unsigned long format(const unsigned long val) { return val; } + 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 { - static const char* string() { static const char *const s = "long"; return s; } - static unsigned int id() { return 10U; } + template<> struct type { + static const char* string() { static const char *const s = "int64"; return s; } static bool is_float() { return false; } - static long min() { return (long)(-1L<<(8*sizeof(long)-1)); } - static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); } - static long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(long)val; } - static const char* format() { return "%ld"; } - static long format(const long val) { return val; } + 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 { static const char* string() { static const char *const s = "double"; return s; } - static unsigned int id() { return 11U; } static bool is_float() { return true; } - static double min() { return -1.7E308; } - static double max() { return 1.7E308; } - static double cut(const double val) { return valmax()?max():val; } - static double inf() { return max()*max(); } - static double nan() { static const double v_nan = std::sqrt(-1.0); return v_nan; } - static const char* format() { return "%g"; } + static bool is_inf(const double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const double val) { +#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.0); 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 { static const char* string() { static const char *const s = "float"; return s; } - static unsigned int id() { return 12U; } static bool is_float() { return true; } - static float min() { return -3.4E38f; } - static float max() { return 3.4E38f; } - static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; } + static bool is_inf(const float val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const float val) { +#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::inf(); } static float nan() { return (float)cimg::type::nan(); } - static const char* format() { return "%g"; } + 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 { + 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::min() || val>cimg::type::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.0L); 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 { + 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::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const long double val) { +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + 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.0); 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 struct superset { typedef T type; }; template<> struct superset { typedef unsigned char type; }; template<> struct superset { typedef char type; }; @@ -2298,8 +2875,8 @@ namespace cimg_library { template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; - template<> struct superset { typedef unsigned long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; @@ -2308,28 +2885,28 @@ namespace cimg_library { template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; - template<> struct superset { typedef unsigned long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef short type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef int type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef short type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef int type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef int type; }; @@ -2337,40 +2914,50 @@ namespace cimg_library { template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; - template<> struct superset { typedef unsigned long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef int type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef int type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef unsigned long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef long type; }; - template<> struct superset { typedef float type; }; - template<> struct superset { typedef double type; }; - template<> struct superset { typedef float type; }; - template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; +#ifdef cimg_use_half + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; +#endif template struct superset2 { typedef typename superset::type>::type type; @@ -2382,35 +2969,58 @@ namespace cimg_library { template struct last { typedef t2 type; }; -#define _cimg_Tt typename cimg::superset::type -#define _cimg_Tfloat typename cimg::superset::type +#define _cimg_Tt typename cimg::superset::type +#define _cimg_Tfloat typename cimg::superset::type #define _cimg_Ttfloat typename cimg::superset2::type +#define _cimg_Ttdouble typename cimg::superset2::type - // Define internal library variables. + // Define variables used internally by CImg. #if cimg_display==1 struct X11_info { - volatile unsigned int nb_wins; - pthread_t* event_thread; - CImgDisplay* wins[1024]; - Display* display; - unsigned int nb_bits; - bool is_blue_first; - bool is_shm_enabled; - bool byte_order; + 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),event_thread(0),display(0), + 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(); @@ -2419,6 +3029,8 @@ namespace cimg_library { #else inline X11_info& X11_attr() { static X11_info val; return val; } #endif +#define cimg_lock_display() cimg::mutex(15) +#define cimg_unlock_display() cimg::mutex(15,0) #elif cimg_display==2 struct Win32_info { @@ -2434,6 +3046,34 @@ namespace cimg_library { #endif #endif + struct Mutex_info { +#if 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; } +#elif defined(_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]); } +#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() { @@ -2443,7 +3083,7 @@ namespace cimg_library { #endif #if cimg_display==1 - // Keycodes for X11-based graphical systems. + // 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; @@ -2534,7 +3174,7 @@ namespace cimg_library { const unsigned int keyPADDIV = XK_KP_Divide; #elif cimg_display==2 - // Keycodes for Windows. + // Define keycodes for Windows. const unsigned int keyESC = VK_ESCAPE; const unsigned int keyF1 = VK_F1; const unsigned int keyF2 = VK_F2; @@ -2625,1249 +3265,1480 @@ namespace cimg_library { const unsigned int keyPADDIV = VK_DIVIDE; #else - // Define unknow keycodes when no display are available. - // (should rarely be used then !). - const unsigned int keyESC = 1U; - const unsigned int keyF1 = 2U; - const unsigned int keyF2 = 3U; - const unsigned int keyF3 = 4U; - const unsigned int keyF4 = 5U; - const unsigned int keyF5 = 6U; - const unsigned int keyF6 = 7U; - const unsigned int keyF7 = 8U; - const unsigned int keyF8 = 9U; - const unsigned int keyF9 = 10U; - const unsigned int keyF10 = 11U; - const unsigned int keyF11 = 12U; - const unsigned int keyF12 = 13U; - const unsigned int keyPAUSE = 14U; - const unsigned int key1 = 15U; - const unsigned int key2 = 16U; - const unsigned int key3 = 17U; - const unsigned int key4 = 18U; - const unsigned int key5 = 19U; - const unsigned int key6 = 20U; - const unsigned int key7 = 21U; - const unsigned int key8 = 22U; - const unsigned int key9 = 23U; - const unsigned int key0 = 24U; - const unsigned int keyBACKSPACE = 25U; - const unsigned int keyINSERT = 26U; - const unsigned int keyHOME = 27U; - const unsigned int keyPAGEUP = 28U; - const unsigned int keyTAB = 29U; - const unsigned int keyQ = 30U; - const unsigned int keyW = 31U; - const unsigned int keyE = 32U; - const unsigned int keyR = 33U; - const unsigned int keyT = 34U; - const unsigned int keyY = 35U; - const unsigned int keyU = 36U; - const unsigned int keyI = 37U; - const unsigned int keyO = 38U; - const unsigned int keyP = 39U; - const unsigned int keyDELETE = 40U; - const unsigned int keyEND = 41U; - const unsigned int keyPAGEDOWN = 42U; - const unsigned int keyCAPSLOCK = 43U; - const unsigned int keyA = 44U; - const unsigned int keyS = 45U; - const unsigned int keyD = 46U; - const unsigned int keyF = 47U; - const unsigned int keyG = 48U; - const unsigned int keyH = 49U; - const unsigned int keyJ = 50U; - const unsigned int keyK = 51U; - const unsigned int keyL = 52U; - const unsigned int keyENTER = 53U; - const unsigned int keySHIFTLEFT = 54U; - const unsigned int keyZ = 55U; - const unsigned int keyX = 56U; - const unsigned int keyC = 57U; - const unsigned int keyV = 58U; - const unsigned int keyB = 59U; - const unsigned int keyN = 60U; - const unsigned int keyM = 61U; - const unsigned int keySHIFTRIGHT = 62U; - const unsigned int keyARROWUP = 63U; - const unsigned int keyCTRLLEFT = 64U; - const unsigned int keyAPPLEFT = 65U; - const unsigned int keyALT = 66U; - const unsigned int keySPACE = 67U; - const unsigned int keyALTGR = 68U; - const unsigned int keyAPPRIGHT = 69U; - const unsigned int keyMENU = 70U; - const unsigned int keyCTRLRIGHT = 71U; - const unsigned int keyARROWLEFT = 72U; - const unsigned int keyARROWDOWN = 73U; - const unsigned int keyARROWRIGHT = 74U; - const unsigned int keyPAD0 = 75U; - const unsigned int keyPAD1 = 76U; - const unsigned int keyPAD2 = 77U; - const unsigned int keyPAD3 = 78U; - const unsigned int keyPAD4 = 79U; - const unsigned int keyPAD5 = 80U; - const unsigned int keyPAD6 = 81U; - const unsigned int keyPAD7 = 82U; - const unsigned int keyPAD8 = 83U; - const unsigned int keyPAD9 = 84U; - const unsigned int keyPADADD = 85U; - const unsigned int keyPADSUB = 86U; - const unsigned int keyPADMUL = 87U; - const unsigned int keyPADDIV = 88U; + // 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; //!< Definition of the mathematical constant PI + const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI - // Definition of a 10x13 font (small size). - const unsigned int font10x13[256*10*13/32] = { - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0, - 0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120, - 0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001, - 0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801, - 0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0, - 0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010, - 0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480, - 0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140, - 0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010, - 0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008, - 0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006, - 0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088, - 0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000, - 0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220, - 0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208, - 0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040, - 0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508, - 0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018, - 0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220, - 0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484, - 0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808, - 0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264, - 0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010, - 0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100, - 0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800, - 0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044, - 0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822, - 0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200, - 0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000, - 0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8, - 0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008, - 0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000, - 0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220, - 0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402, - 0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980, - 0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421, - 0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020, - 0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701, - 0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0, - 0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c, - 0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0, - 0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000, - 0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000, - 0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000, - 0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0, - 0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040 - }; + // Define a 12x13 font (small size). + static const char *const data_font12x13 = + " .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w Fw " + " mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxlwlwkwuwnwuynwuwTwlwlwtwnwtwnw my Qw +wlw b" + "{ \\w Wx`xTw_w[wbxawSwkw nynwkyw bwswcw" + "kwuwjwuwozpwtwuwnwtwowkwjwmwuwuwkwIxmxuxowuwmwswowswmxnwjwhwowswowsw0wmwowswuwnwrwowswpwswowkwjwrwqw" + "rwpwkwkwtwnwkxsxqxswowswpwswnwswpwswowrwnwmwrwqwqwqwswswrwswowswjwpwlxjwkxuxLw[wcw_wSwkw mw\"wlwiw=wt" + "wmxlwFw cwswnwuwnwkwjwswo{pwrwpwtwtwpwswby`w`yUwlwtwpwqwpwswowlw\\wrwrxuwHwrwfwuwjwlwlwTyuwVwlwtwawsw" + "owswowswcwuwmwuwmwuwmwuwmwuwlwkwuwnwswpwkwkwkwkwkwkwkwkwswoxswowswowswowswowswowswowrwpwswpwrwpwrwpw" + "rwpwrwpwswoznwtw Ww (wGwtwtwqwqwqwuwuwuwqwswuwqwqw=wqxtw`{nzp~q{ozowrwnxmwtwow bzawkwuwl}rwuwnwtwuw" + "nwtwowkwjwlyjwIwlwswmwiwkwnwuwnwkwhwnwswowswowkwewewixnwsytwswuwnwrwpwkwrwpwkwkwkwrwpwkwkwuwmwkxsxqw" + "uwtwpwqwqwswowqwqwswowiwmwrwpwswowtwtwpwuwmwuwjwowkwjwlxsxXynzmymznyozlzoznwkwkwtwnwkzuyrzmynzmzowux" + "myozmwswpwrwowtwtwrwrwpwrwp{mwlwiwHyuwpwtwkwmxlynzoxswmwmwswnwswowtxq|owtwtwpym{p{owswnwuwmwlwkwqwqx" + "uwuxqwrwpwtwtwqwqwowlwuwuwkwmwlwtwowuwuwdwjznwl{nwuwnwkx_wtxtwswtwlwtwWwuytwgyjwmwjwawswoyuwVwlwtwnw" + "twmwtwnwtwmwuwmwlwuwmwuwmwuwmwuwmwuwmwuwmxuwowkwkwkwkwkwkwkwkwkwrwpwuwtwpwqwqwqwqwqwqwqwqwqwowtwpwsw" + "uwqwrwpwrwpwrwpwrwowuwnwswowuwlymymymymymymyuyqymymymymynwkwkwkwjynzmymymymymykwmzowswowswowswowswpw" + "rwozowrwW}q}qwtwtwqwtwtwqwtwtwA}rwuw_{p~r~r}pwtwowrwnxmwtwow aw_w]wtwpwuwmxuwmybwjwlyjwIwlwswmwiwnyn" + "wtwnznzkwmynwswTyp}pylwmwtwtwtwswuwn{owkwrwp{o{owk|pwkwkxlwkwuwuwuwqwuwtwpwqwqwswowqwqwswoykwmwrwpws" + "wowuwuwuwowkwjwnwkwjwDwowswowkwswowswowkwswowswowkwkwuwmwkwswswswswowswowswowswoxlwswowkwswpwrwowtwt" + "wqwtwowrwlwoxkwhxVxuxpwtypwuwjwnwtwnwkwswowtxnxmwswowqwqwtwuxqwtwnwtwtwqwswowswmwm{nwuwlxnwkwqwqwtwt" + "wqwrwpwtwtwqwuyuwpwiwhwnwmwrwnwbwkwuwlwlwswoxuxowlwtw`wuwrwszmwtwo}dwuwtwuw[}qymx`wswoyuwow_ylxlwtwo" + "yuwoyuwoyuwmwlwuwmwuwmwuwmwuwmwuwmwuwmwt{swk{o{o{o{owkwkwkwlztwpwuwtwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwr" + "wpwrwpwrwpwrwnwmwswowuwiwkwkwkwkwkwkwswswkwswowswowswowswowkwkwkwkwswowswowswowswowswowswowswcwtxows" + "wowswowswowswpwrwowswpwrwWwtwtwqwqwqwuwuwuwqwuwswqwqw>wowuw`}q~q|q}qwrwpwrwowtwnwtwo~ izaw]wtwoykwux" + "qwtwswfwjwmwuwuwn}eyaxlwswmwjwjwpwswjwowswmwmwswnzWy]ypwlwtwtwuwswswowrwpwkwrwpwkwkwsyqwrwpwkwkwuwmw" + "kwuwuwuwqwtwuwpwqwqznwqwqzkynwmwrwowuwnwuwuwuwowkwjwnwkxkwGzowswowkwswo{owkwswowswowkwkxlwkwswswswsw" + "owswowswowswowjxmwkwswowtwnwuwuwuwpxmwtwlwlwlwiwlytwewtwtwqwswowtxoznwswnxmwswnwuwmwuwnwswowtwtwqwtw" + "twqwtwnwtwtwqwswowswmwmwswowswmwmwkwqwqwtwtwqwrwowuwuwpwuyuwq~own~own~owbwkwuwmznwswmwbwswawuwrwgwtw" + "hwdwuytwXwJwswnxuw=wtwmwswowtxowswqxmwswowswowswowswowswowswnwtwowkwkwkwkwkwkwkwkwkwrwpwtwuwpwqwqwqw" + "qwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowtwmznznznznznzn~swk{o{o{o{owkwkwkwkwswowswowswowswowswow" + "swowswo}qwuwuwowswowswowswowswowtwnwswowtwUwuwuwowswowswowswowsw@}qx`}q~pzo{pwrwpwrwowtwnwtwow aw_w_" + "}owuwmwuwtwrwswuwewjwkwiwJwkwswmwkwiwp|kwowswmwmwswkwWym}mypwlwszr{owrwpwkwrwpwkwkwqwqwrwpwkwkwtwnwk" + "wtwtwqwtwuwpwqwqwkwqwqwtwiwnwmwrwowuwnwuwuwuwpwuwlwkwmwjwkwHwswowswowkwswowkwkwswowswowkwkwuwmwkwsws" + "wswswowswowswowswowhwnwkwswowtwnwuwuwuwpxmwtwmwkwlwiwmwtydwtwtwqwswowswowtwnwswowkwswnwuwnwtwnwswowt" + "wtwqwtwtwqwtwnwtwtwqwswowswmwmwswowswnwlwkwqwqxuwuxqwrwnyowqwpwiwhwpwuwuwowrwpwuwuwdwkwuwlwlwswo{owk" + "xuwawtxtwszmwtwiwdwuwtwuwXwJwswmwuwKzmwtwlwtxowrwpwtxrxl{o{o{o{o{o{o{owkwkwkwkwkwkwkwkwkwrwpwtwuwpwq" + "wqwqwqwqwqwqwqwqwowtwpwuwswqwrwpwrwpwrwpwrwnwmznwswowswowswowswowswowswowswowswowkwkwkwkwkwkwkwkwkws" + "wowswowswowswowswowswowswcwuwuwowswowswowswowswowtwnwswowtwTymymymymy=wmw^wuwuwmxlxmyowrwowtwnwtwmxm" + "w bwswIwuwmwuwmwuwtwrxswdwjw]wJwkxuxmwlwlwswlwjwowswmwmwswlwSycyawlwswowrwowswpwswowkwjwrwqwrwpwkwkw" + "swowkwqwqwsxowswpwjwswpwswowrwnwmxtxnwlwswpwswmwlwlwjwkwHwswowswowkwswowswowkwswowswowkwkwtwnwkwswsw" + "swswowswowswowswowkwswowkwswnxlwswpwtwmxmwjwlwiwTxuxpwtxowswowtwnwswowkwswnynwtwnwswowtwtwqxuwuxqwtw" + "nwtwtwqwswowswmwlwuwnwswowkwjwswo{pwrwmwmwswnwjwiwnymwtwnycwkwuwlwl{mwmwiw_wrwdwtwVwrw*wswmwuw?wtwlw" + "tzqwrwpwtzswkwswowswowswowswowswowswowswnwswpwkwkwkwkwkwkwkwkwswowsxowswowswowswowswowswowrwpwswpxtx" + "pxtxpxtxpxtxnwmwkwswowswowswowswowswowswowswowtxowkwswowswowswowswowkwkwkwkwswowswowswowswowswowswow" + "swlwnxtwowswowswowswowswnxmwswnx >wlw\\wkx`wnwrwoznwtwmxl| gybw^wtwozmwsxpzuxfxlx]wnw_wlxjyn{o{nykwnz" + "mymwkynymwkwewewjwjwrwswqwp{myozn{owizpwrwpwkwkwrwp{owqwqwsxnyowiyowrwozmwlzmwlwswqxsxnwm}qwjxlwGzoz" + "mymznynwjzowswowkwkwswowkwswswswswnynzmzowjymxlznxlwswqwrwnwm{mwlwiwHxuxpzmxlymynwswmwnwrwozmxuxo{pw" + "txn{pzmykwmyo}p{owkyuynwnwrwmwly`w_w_wbwjzo{pwqwnwmwhw_z>zY}M|nwuw2wqwqwryrwqwqyowqwqwqwqwqwqwqwqwqw" + "qwqwqwr{qyo{o{o{o{owkwkwkwkznwsxnymymymymycwuynznznznzmwmwkwuynznznznznznznyuzrymymymymynwkwkwkwjynw" + "swnymymymymybzmznznznznwlzmw hwHwlwSwTw {+qnrmqapmp Kpepgpiuhpephscqfqhqfqhqfqhqfqhqfqhqfqhqixgudxdxdxdxdq]q]q]q]wcqjr" + "bt`t`t`t`taphpgplt`s_s_s_s_q`q]qmsctnqctnqctnqctnqctnqctnqbsktgs_uauauaucq]q]q]q[saqjqbs_s_s_s_sNpms" + "_snqbsnqbsnqbsnqaq`qns_q !p Zp jp#q\\q6q7q lq [sjq Qq -q OqZq]q Cq;q HqWq $rIq`qZq _q iqbqK" + "qFqIq`q hp$q]u JqYpmpLp .p jp ]p Xr`q[r !p Tp\"p\\p6q6q mq Yx Qr -r Ps\\q_s" + " Ipkq:q HqWq $qHq`qZq _q iqbqKqFqIq`q hp$q]t IqYpmpLq /q kq Fq_q[q #s Tp\"q" + "^q6p 1p Vu Rs YsJsMy &v])]2_4^U^ 6^T\\5])]1_2]T\\8^U^ K])]2`4^V^3] " + " " + " U]*\\2a4`V\\8^U^5a F]*\\1\\X\\4^U^=]*\\" + "2a5^U^ 7aV\\4]*\\1a4`V\\8^U^ J]*\\1\\X\\4^V^3\\ " + " " + " S],\\1\\W\\5g8^U^6c F],\\1\\V\\5^U^<],\\2]W]6^U^" + " 8h3],\\0\\W\\5g8^U^ I],\\1\\V\\5^V^4\\ ;] " + " " + " :\\-]2\\U\\6\\V`7^U^7]U] F\\-]2\\T\\6^U^;\\-]3]U]7^U^ 8\\" + "Va1\\-]1\\U\\6\\V`7^U^ H\\-]2\\T\\6^V^5] =a J] " + " " + " N\\/]2\\S\\7\\T]6^U^7\\S\\ E\\/]2\\R\\7^U^:\\/]3]S]8^U^" + " 8\\T^/\\/]1\\S\\7\\T]6^U^ G\\/]2\\R\\7^V^6] =c L^ " + " *^ U` " + " O^ )\\S\\ !^$^3\\ E]" + "U\\ K^$^4^ G^$^4] J^$^3\\ #^$^3\\ 4^ B[ " + " &^ Xe " + " S^ (\\S\\ )Z Q^&^3^2]S\\ A\\S\\ K^&^3^ F^&^4_ >]S" + "\\9^&^3^2]S\\ W^&^3^ 6^ Q] M[ ?` ![1^H]?` =]4](\\ %` >b4c Bb " + "?`2a .a Ib Pb Aa `0`*^ $^.` <^F]F^F]G`G] " + " F\\S\\ ;b %a2a2a2a2a a:].a !^T_ Bg ` Dd2_8n?" + "m7g3]:rD]P]P]@g <] 8] 8] B] 3e J^K^ If7^U^+b@d Fb@f5a Ad4e-] :f Ra0d AaF\\HaF\\HeJ\\?]._0_" + "0_0_0_2\\U\\0tHh@n?n?n?n?].].].]-h:_J]w " + "P[ 9[/a:aQa7[ Wl \"h E]1]T]+\\R\\;[4dL]Ag=])]2])\\ U^1f8c8k;j1`;k7h?n;h9g 5i*b:_" + "8k6kBl=n?l7mD]H]C].].]L_A].`I`H`K]>kAj6kAj9kBuB]H]F]E]E^L_L^R^L^D^I^BrBb7^+b(a D] ;] '] Gd" + " A].].].].] ;] (b:].b #^Q] Dj !a Ff3_8n?m8i4]:rD]P]P]Bk ?_ 9] 9_ C]&[0f " + "I]K]=]0g7^U^-fC\\S] IfBf6c B[S]5[S].] `K]>k]*]3]W]6^U^._V_;]Wa5]*]2\\V\\6]Wa7^V^ I]*]2\\V\\5^V^2]7]+^V^ @]W\\=v P[ 9\\1c_8m:`R`Cn?n?l9`QaE]H]C].].]M_@].aKaH`K]?`S`Bk8`S`Bk;_R_BuB]H]F]E]D]MaM]P]L]B^K^ArB]1]&])" + "c D] <] '] G] :].].].].] ;] (^6]*^ #]P^ E^P\\ V^ H^T^4_8n?m:`S`6]:rD]P]P" + "]C`S` Aa :] :a D]&[1^S\\ I^M^=]0^R[7^U^/^R^EZO\\ L^R^ N]U] :],\\0] \\H]B\\H]=\\M]>" + "]._0_0_0_0_0_/uK`R`Cn?n?n?n?].].].]-n@`K]?`S`>`S`>`S`>`S`>`S` H`ScE]H]C]H]C]H]C]H]E^K^@],^" + "T^5],]1\\V\\6\\U`7^V^6]U\\ F],]2\\T\\6^U^=],]2\\U\\6^U^-e9\\U`4],]1\\V\\6\\U`7^V^ H],]1\\V\\5^V^3]6]+^" + "V^ B`1`1`1`1`6]W]>u P[ 9]2e>eUf;^ %q $^O\\ F]1]T],]S];[5]T]N\\@]P[=]*]0]2ZR\\RZ $" + "]2]P]<_W]8]N]\\H\\A\\H\\<\\M\\=]/a2a2a2a2a1_/]V];_M]C].].].].].].].]" + "-]ObBaL]@^M^@^M^@^M^@^M^@^M^ J^N`D]H]C]H]C]H]C]H]E^K^@]-^Q]5].]1\\T\\7\\S]6^V^5c E].]2]S\\7^U" + "^<].]2\\S\\7^U^,a6\\S]2].]1\\T\\7\\S]6^V^ G].]1\\T\\6^V^4]5]+^V^ De6e6e6e6e9\\U\\>u P[ :_3f@gVf<" + "_ &r $]M[ F]1]T],\\R]>d<^T^P]A^OZ=]+].]4]T\\T] &^3^P^=[S]8[K].]4\\X];],]!]<]N]>^O^ " + " 8ZM^3`P`Ba9]M^=^J\\C]K_B].],^H\\E]H]C].].]O_>].aKaHaL]A^K^D]N^<^K^D]N^>]JZ6]6]H]E]G]C]MaM]" + "O^P^@^M^-^A]1]&]+_W_ D] >] '] H] 9] B].] ;] )]4](] %]N]:c6] G] J^P^7a8" + "_1],^K^;c=]H]D]P]P]E^K^ Ee <] " + "\\I]A\\I]<\\N]=]/a2a2a2a2a2a1]U]<^J\\C].].].].].].].]-]K_CaL]A^K^B^K^B^K^B^K^B^K^ K]K^D]H]C]H]" + "C]H]C]H]D^M^?]-]P]4]0]1\\R\\ Ha C]0]2]R] E]0]2\\Q\\ 9c 9]0]1\\R\\ !]0]1\\R\\ ?]4] Di:i:i:i:i" + ";\\6]G] P\\ :`5g@gWh>a (_ J]KZ F]1]T],\\R\\?h>]R]P\\@]1]+].]3^V\\V^.] T]2]N]5]8ZJ]-]" + "6]X];]-]!^=]L]?]M] *]5_J_Ec:]L^>]H[C]I^C].],]F[E]H]C].].]P_=].]X]M]X]HbM]A]I]D]M]<]I]D]" + "M]?]%]6]H]E]G]C^NaN^N]Q^>^O^-^@]0]'],_U_ &] '] H] 9] B].] ;] )]4](] %]N]:d7] " + " F] K]N]8c8^1],]I]>i@]H]D]P]P]E]I] Fg =] =g G]&[2] <]O];]1] 1\\F\\=\\ Q\\F\\ S\\Q\\+]3\\." + "] IeU\\ M\\3\\N\\ ?\\I\\@\\I\\=]M\\<]0c4c4c4c4c3a1]U]<]H[C].].].].].].].]-]J_DbM]A]I]B]I]B]I]B]I]" + "B]I] L]J_E]H]C]H]C]H]C]H]C^O^>].]N] .] '`X_ I] FbWa=bWa=bWa=bWa=bWa<" + "\\6^I^ ?Z2[ :a5gAiXh?c *^ H] 7]1]T]-]S]Aj>]R]Q]@]1],],\\1^X\\X^,] T]3]L]6]'].]7]W]" + ";]-]!]<]L]?]M^ +]6^F^F]W]:]K]?]FZC]H^D].]-]DZE]H]C].].]Q_<].]X]M]X]H]X]M]B]G]E]M^>]G]E]" + "M^@]%]6]H]E^I^B]O^X]O]M^R^=]O^-^@]0]']-_S_ '] '] H] 9] B].] ;] )]4](] %]N]:e8" + "_ H] L]M]8]W]7^2]-]G]AmB]H]D]P]P]F]G] Hi >] >i J[3] ;^Q^;]1] 2\\RbT\\Ge R\\VdR\\ T\\" + "Q\\+]4\\2a IfU\\ M\\3\\N\\ ?\\J\\?\\J\\AaM\\ G]W]4]W]4]W]4]W]4]W]4c3^U]=]FZC].].].].].].].]-]H]D]X]" + "M]B]G]D]G]D]G]D]G]D]G]A[H[B]J`E]H]C]H]C]H]C]H]B]O^>g8]N] 1]T_ 3[ 9] " + "G_O^?_O^?_O^?_O^?_O^=\\5]I^ @\\3[ ;c6gAy?d7`8]L]7^7]L]>^ H] 6]1]T]-]S]B_W[U]>]R]R]?]1" + "],],]0d*] T]3]L]6]'].]7\\V];].] ]<]L]@]K] 7Z PZ X]7^D^G]W]:]K]?]/]G]D].]-]/]H]C].].]R_;]" + ".]X^O^X]H]X^N]B]G]E]L]>]G]E]L]@]%]6]H]D]I]A]O]W]O]L^T^<^Q^-^?]0]'].^O^ Sb7]U`2b4`U]8a8])`" + "7]T_ M].]%_O_@_2`0`3`/_3c9] )]4](] N_6]N]3^7a/c0_ <^ D[U^ Ga N]L]9]W]6^3]-]G]B`W" + "]W`C]H]D]P]P]F]G] I_X]X_ ?] ?_X]X_ Nb7]2ZFZ=]Q]:]0] 3[SfU[Ig R[UfS[ T\\Q\\+]5]2a IfU\\ M" + "\\3\\N\\ ?\\K]?\\K]AaN] G]W]4]W]4]W]4]W]4]W]4]W]3]T]=]/].].].].].].].]-]G]E]X^N]B]G]D]G]D]G]D]G" + "]D]G]B]J]C]KbF]H]C]H]C]H]C]H]B^Q^=j;]P_9b3b3b3b3b3b3bN`Bb3a2a2a2a V_2_2`1`1`1`1` ;aU] " + " :]U` S^T]U^A^L^A^L^A^L^A^L^?]5]I] @^5\\ ]R]R\\>]1],],].`(] U^3]L]6]'].]8]V];].]!^<]L]@]K] :] P]#^8^A]I^W^;]K]@].]G^E].].].]H]" + "C].].]S_:].]W]O]W]H]W]N]C]E]F]L]?]E]F]L]@]%]6]H]D]J^A]O]W]O]L^U^:^S^-^>]0^(]/^M^ Wh:]Wd6f" + "8dW]:e>h2dW]?]Vd<].].]O_>].]WdScK]Vd8f;]Wd7dW]?]Wa6h>h6]L]B]I]A]P`P]K^L^B^K^@l4]4](] PdU" + "]A]N]2^8e5g;]Vd?^J^8]6]L] E]V`>pA]S]S]:e6kDo>]L]:^W^6^4].]E]D_U]U_D]H]D]P]P]G]E] K_W]W_ @]" + " @_W]W_ Qf9]3\\H\\>^S^:]0_ 6[ThT[K]Q\\ S[T\\R]S[ U]S]+]6],] ?]L]@fU\\ M\\3\\N\\ ?\\K\\>\\K\\;]O\\ G" + "^W^6^W^6^W^6^W^6^W^5]W]4^T]>].].].].].].].].]-]G^F]W]N]C]E]F]E]F]E]F]E]F]E]D_L_E]K]W]F]H]C" + "]H]C]H]C]H]A^S^^K^ O]S]S]B]I]B]I]B]I]B]I]@]5^K^ @]4[ ;f8gAyAg] F] 6]1]T]" + "-\\R\\B]T[6]R]S]>^2]-]*\\.`(] U]2]L]6]'].]9]U];].]!];]L]@]K] =` P`'^7]?\\I]U];]K]@].]F]E].]" + ".].]H]C].].]T_9].]W]O]W]H]W^O]C]E]F]L]?]E]F]L]@]%]6]H]C]K]@^P]W]P^K^V^9]S]-^=]/](]0^K^ Xi" + ";]Xf9h9fX]h6]L]A]K]@^Q`Q^J^N^@]K]?l4]4](]" + " QfW^A]O^1]6f9h;]Xg@_K]7]6]L]=]G]C^Wc@pA]S]S]]L]:]U]5^5].]E]E^S]S^E]H]D]P]P]G]E]@" + "Z+]V]V^-Z4]5ZKZ:]V]V^ Sh9]4^J^>]S]9]._ 8[U_Q[T[L]P\\ S[T\\Q]T[ T]U]*]7]*] @]L]@fU\\ M\\3\\N" + "\\ ?\\L]>\\L]:]Q]:]1]U]6]U]6]U]6]U]6]U]6^W^5]S]>].].].].].].].].]-]F]F]W^O]C]E]F]E]F]E]F]E]F]" + "E]C_N_D]L^W]F]H]C]H]C]H]C]H]@]S];]P_=]S^8i:i:i:i:i:i:iVgIh9h9h9h9h<].].].]'d<]Xg:h9h9h9h9h" + "0^8k?]L]?]L]?]L]?]L]A]K]>]Xf>]K] O]R]R]D]G]D]VZOZV]D]KZV]D]G]A]4]K] @]3[ j=]L]8" + "`7]N]?] F^ 6]1]T]5uI]T[6]R]S\\<^3]-]*]1d*] U]3]J]7]'].]9\\T];].\\Ua-^;]L]@]K^?].] Uc " + "Pc+_8]>]J]U];]K]@].]F]E].].].]H]C].].]U_8].]W^Q^W]H]V]O]C]E]F]L]?]E]F]L]@^&]6]H]C]K]?]Q^V]" + "Q]I^X^8^U^.^<]/](]1^I^ ]R_h6]L]A]K]?]Q`Q]H^P^?]K]?l4]4](] R^U^W]@]O]0^7g;_S];bT^@`L]8_7]L]>]E]E^W]V]@pA]S]S]" + "=_T_].].].].].].].].]-]F]F]V]O]C]E]F]E]F]E]F]E]F]E]B_P_C]L]V^G]H]C]H]C]H]C]H]@^U^;]N^>" + "]T]6]R_;]R_;]R_;]R_;]R_;]R_;]R_X_T^K_R\\:_S^;_S^;_S^;_S^=].].].]*h=bT^;_T_;_T_;_T_;_T_;_T_1" + "^9_T`>]L]?]L]?]L]?]L]A]K]>aT_?]K] P]Q]R]E]F]E]V\\Q\\W]E]K\\W]E]F]A]4^L] A^@ZN\\ =i8e@yCk?^R^" + "=]L]9b8]O^?] Im B]1]T]5uI]T[6]S^T]<^3]-]*]3^X\\X^,] V^3]J]7](^/]9]T];e7]We/]9]N]?]K" + "^?].] Wd Nd._8]O`U\\T\\K]S]<]L^A]-]F^F].]/]-]H]C].].]V_7].]V]Q]V]H]V^P]D]C]G]L]@]C]G]L]?^']6" + "]H]C^M^?]Q]U]Q]Ic6^W^._<]/^)]2^G^ !ZM^=`Q^=^NZ;^Q`>^P^=].^Q`?`Q^>].].]R_;].`R^X\\R^M`Q^=^P^" + ">`Q^=^Q`?`1]MZ;].]L]A^M^?]Q`Q]G^R^>^M^1^4]4](] D]P^A]R^X]@]P^/]9^Vb=^NZ;`Q^AaN^8_7]L]>]E]" + "F^V]U]>]P]>]S]S]>^P^>`T`7]6]J]<]S]5^6]/]C]G]Q]Q]F]H]D]P]P]H]C]C^&]TZ,^7]7^N^6]TZ H]/^U[TZ9" + "]2n;]U]8]0d <[U]F[M\\P]2[R[ M[S\\P\\S[ Tb(]9]'\\ @]L]@fU\\ M\\3]P]9[R[1\\M\\<\\M\\7\\R\\8]2]S]8]S]8]" + "S]8]S]8]S]7]U]6]R]?]-].].].].].].].]-]F]F]V^P]D]C]H]C]H]C]H]C]H]C]B_R_C]L]T]G]H]C]H]C]H]C]" + "H]?^W^:]M]>]U^6ZM^].].].]+i=`Q^=^P^=^P^=^P^" + "=^P^=^P^2^:^P^>]L]?]L]?]L]?]L]A^M^>`Q^@^M^ P]Q]Q]F]E]F]W^S^W]F]L^W]F]E]B]3]M^ B^B^O[ =k8" + "d?xClA^P^>]L]9]X]8^P]>\\ Hl A] 9uI]T[5]T]T]:^ =]*]5^V\\V^.] V]2]J]7](]/^:]S];h:]Xg0]" + "9^P^?]K^?].]!e Je2_7\\PdW\\S\\L]S]<]M^@]-]E]F].]/]-]H]C].].]X_5].]V]Q]V]H]U^Q]D]C]G]L]@]C]G]M" + "^?`)]6]H]B]M]>]Q]U]Q]Hb5c-^;].])] B]=_O]=].]O_>]N^>].]O_?_O]>].].]S_:]._P`P]M_O]=]N]>_O]" + "=]O_?_1]-].]L]@]M]>]RbR]G^R^=]M]1^3]4](] FaSaD^Qa?]R_.]9]R`>]._O]>^N]8`7]L]>]E]G^U]U^?]P]" + ">]S]S]>]N]>^P^7]6]J]<]S]4^7]/]C]G]Q]Q]F]H]D]P]P]H]C]D_&]&_8]8_N_7] B]/]T[3]1l:^W^8]1]W` >\\" + "U\\E\\N\\P]3\\S\\ N\\S\\P\\S\\ S_']:]&\\ @]L]@fU\\ M\\2\\P\\8\\S\\2\\N]<\\N]7\\S]8]2]S]8]S]8]S]8]S]8]S]8]S]" + "7]R]?]-].].].].].].].]-]E]G]U^Q]D]C]H]C]H]C]H]C]H]C]A_T_B]M]S]G]H]C]H]C]H]C]H]>c9]M^?]U]']" + ".].].].].].`O^N].]N^>]N^>]N^>]N^?].].].],_R^>_O]=]N]=]N]=]N]=]N]=]N]2^:]O_?]L]?]L]?]L]?]L]" + "@]M]=_O]?]M] O\\P]Q]F\\D]F\\U^U^V]F\\L^V]F\\D]B]3]M] RuJ`O[ >m9c>wCmA]N]>]L]9]X]7]P]?] " + "Im A] 2\\R\\A]T[5^V^T\\:` ?](\\6]T\\T]/] V]2]J]7])^1_9]S];i;bS^2^8^S_>]K^?].]$e@u@e6_7]QfX\\S\\" + "M^S^=]N^?]-]E]F].]/]-]H]C].].c4].]U]S]U]H]T]Q]D]C]G]M^@]C]G]M]=c-]6]H]B]M]>^R]U]R^G`4c.^:]" + ".])] B]=^M]?^/]M^?]L]>]/]M^?^N^?].].]T_9].^O_O^N^N^?]M^?^M]?]M^?^0]-].]L]@]M]>^S]X]S^F^T" + "^<^O^2_3]4](] GcUcE]Pa?]Vb-]:]O_?].^N^>]O^8a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@^N^8]6]J]=^S^4^8" + "]/]C]H^Q]Q^G]H]D]P]P]H]C]E_%]%_9]9_L_8] B]0^T[3]0_T_>cWc=]1]U_ ?[U\\C[N]R^4]T] N[R\\Q]R[ 'u" + "G]&] @]L]?eU\\ M\\2]R]8]T]3\\N\\;\\N\\7]S\\7]3^S^:^S^:^S^:^S^:^S^9]S]8^R]?]-].].].].].].].]-]E]G" + "]T]Q]D]C]H]C]H]C]H]C]H]C]@_V_A]N]R]G]H]C]H]C]H]C]H]>c9]L]?]U]'].].].].].]._M]O^/]L]?]L]?]L" + "]?]L]?].].].]-^O]>^N^?]M^?]M^?]M^?]M^?]M^ I]O`?]L]?]L]?]L]?]L]@^O^=^M]@^O^ P]P]P\\G]C\\G]T^" + "W^T\\G]M^T\\G]C\\B]3^O^ RuJ[X]P[ >o=\\XaX]BwDoC]L\\>]L]:^X^8]P]?] E] 5] 3]S]A^U[4dT];b @" + "](]6ZR\\RZ.] V]2]J]7]*^7d8]R];]R_]-]E]Fm>k=]-rC].]" + ".b3].]U]S]U]H]T^R]D]C]G]M]?]C]G]N^^M]?].]M^?]L]>]/]M" + "^?^M]?].].]U_8].^N^N]N^M]?]L]?^M]?]M^?^0]-].]L]@^O^=]S]X]S]D^V^:]O]2_2]4](] H\\U^W]U\\E]Pa?" + "]Vb-];]M^?].^M]>^P]7a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@]L]8]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV" + "]F_$]$_:]:_J_9] B]0]S[3]0]P]>o=]2]S_ @[U\\C[M]T_5^U^;u O[R\\R]Q[ 'uH]/ZQ] ?]L]?eU\\ M\\1]T]7^" + "U^4\\O]O]I\\O]T`MZQ]S]O]E]3]Q]:]Q]:]Q]:]Q]:]Q]:^S^9]QmO]-m>m>m>m>].].].]1hL]G]T^R]D]C]H]C]H]" + "C]H]C]H]C]?_X_@]O]Q]G]H]C]H]C]H]C]H]=a8]L]?]U]&].].].].].].^M]O].]L]?]L]?]L]?]L]?].].].].^" + "M]?^M]?]L]?]L]?]L]?]L]?]L] I]Pa?]L]?]L]?]L]?]L]?]O]<^M]?]O] O]P]P\\G]C\\G]ScS\\G]N^S\\G]P]P\\B" + "]2]O] QuF]Q[ >oAqDuDqD]L]?]L]:^X^8^R^?\\ D] 5] 3]S]@`X[3bS\\R^G]W^N] P](].\\&] W]1]J" + "]7]*^7c8]Q];ZM^=`O^4]4d:]M_?].])d:u:d=_5\\R]O^R\\N]Q]=j<]-]E]Fm>k=]-rC].].a2].]U^U^U]H]S]R]D" + "]C]G]N^?]C]G]P_:g3]6]H]A]O]<]S]S]S]E^1_.^8]-]*] A]>^M]?]/^M^?]K]?]0^M^?]L]?].].]V_7].]M]" + "M]N]L]@^L]?^M]@^M^?]/]-].]L]?]O]<]S]X]S]C^X^9]O]2^1]4](]0_IZ O[R\\X]S\\G^O_>]Vd9_U];]L]?].]L" + "]=]P]8]X^9]L]?]C]I^T]S]@]P]>]S]S]?]L]@]L^9]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]G_#]#_;];_H" + "_:] B]0]S[3]0\\N\\>o=]2]Q^ A[U\\C[LcX\\6]T]9u O[RfP[ 'uIf7e >]L]>dU\\<] :f5d4]T]:fT\\O^NfT\\UdOeR" + "\\O^F^3]Q]:]Q]:]Q]:]Q]:]Q]:]Q]:^QmO]-m>m>m>m>].].].]1hL]G]S]R]D]C]H]C]H]C]H]C]H]C]>d?]P^Q]G" + "]H]C]H]C]H]C]H]<_7]L]?]U^'].].].].].].^L]P].]K]@]K]@]K]@]K]@].].].].]L]?]L]@^L]@^L]@^L]@^L" + "]@^L] I]Q]X^@]L]?]L]?]L]?]L]?]O]<^M]?]O] O\\WmX]H\\WmX]H\\QaR]H\\N^R]H\\O]P]C]2]O] QuF]R\\ ?qC" + "sDtDrE]L]?]L]:]V]7]R]>x '] 5] 3\\R\\?e3^R\\SbJ^V^O] P](].\\&] W]1]J]7]+^6e:]Q]-^>_M]5^6" + "h<^O` Qe8u8e@^5]R\\M]R\\O^Q^>m?]-]E]Fm>k=]KdFrC].].b3].]T]U]T]H]S^S]D]C]G]P_>]C]Gk6f5]6]H]A" + "^Q^<]S]S]S]F_1_/_8]-]*] A]>]K]A].]K]@]J]?]0]K]?]L]?].].]W_6].]M]M]N]L]@]J]@]K]A]K]?]/^.]" + ".]L]?]O]<]T^W]T]C^X^9^Q^3^1]3]']3dN\\ P\\R`Q[G]N_>]Q`;bW];\\K^?]/]L]=]Q^8]W]9]L]?]C]I]S]S]@]P" + "]>]S]S]@]J]B^L^9]6p>^Q^4^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]H_\"]\"_<]<_F_;] B]1]R[3]1]N]8a6]2]P^ B" + "[U\\C[K`V\\7]T]8u O[RdN[ 'uIf5a <]L]=cU\\<] :f3`1]T];fU\\N^NfU\\T[S]NaQ\\N^G^3^Q^<^Q^<^Q^<^Q^<^Q" + "^;]Q]:]PmO]-m>m>m>m>].].].]1hL]G]S^S]D]C]H]C]H]C]H]C]H]C]=b>]P]P]G]H]C]H]C]H]C]H]<_7]L]?]U" + "_(].].].].].].]K]Q].]J]A]J]A]J]A]J]@].].].].]L]?]L]@]J]A]J]A]J]A]J]A]J] K]P\\V]@]L]?]L]?]L]" + "?]L]?^Q^<]K]@^Q^ O\\WmX]H\\WmX]H\\P_Q]H\\O^Q]H\\O]P]C]2^Q^ D^<]R[ >qDuEsCqD]L]?]L]:]V]7]R]>x " + " '] 5] 3\\R\\=f+]TdL^T^P] P](].\\2u *]1]J]7],^-_=]P],]>_M]5]7_R^<^Qa Sd .dC^4\\R]M]R\\O]O" + "]>]N_@]-]E]F].]/]KdF]H]C].].]X^4].]T]U]T]H]R]S]D]C]Gk=]C]Gj1c6]6]H]@]Q];^T]S]T^Ga1].^7]-]*" + "] Lh>]K]A].]K]@]J]?]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]._0].]L]>]Q];^U]V]U^Bb7]Q]" + "3^1^3]'^6iS^ P[P^P[G]N_>]N^=dX]<]J]>^1]L]=^R]8^W]9]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]>]O" + "]5^8]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]]K]@]O[X\\I`3]O]<]O]<]O]<]O]<]O]<]O];]P]?]-].].].].].].].]-]E]G]R]" + "S]D]C]H]C]H]C]H]C]H]C]<`=]Q]O]G]H]C]H]C]H]C]H];]6]L]?]T_4h9h9h9h9h9h9hK]Q].]J]A]J]A]J]A]J]" + "@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]Q\\U]@]L]?]L]?]L]?]L]>]Q];]K]?]Q] N\\WmX]H\\WmX]H\\P_" + "Q]H\\P^P]H\\O]P]C]1]Q] C]:]S[ ?sEvEqAoC]L]?]L];^V^8^T^>x '] 5] 4]S]]K]A].]K]@p?]0]K]?]L]?]" + ".].b3].]M]M]N]L]@]J]@]K]A]K]?].c4].]L]>]Q]:]U]V]U]@`6^S^4^5b2]&b^Ua<]J]=" + "c7]L]<]S^8]V^:]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?^O^7^7]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]\\I\\@\\O\\X\\J`" + "3^O^>^O^>^O^>^O^>^O^=]O]<^P]?]-].].].].].].].]-]E]G]R^T]D]C]H]C]H]C]H]C]H]C];^<]R]N]G]H]C]" + "H]C]H]C]H];]6]L]?]S`8j;j;j;j;j;j;|Q].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]R]U]@]" + "L]?]L]?]L]?]L]>^S^;]K]?^S^ N\\WmX]H\\WmX]H\\QaR]H\\Q^O]H\\O]P]C]1^S^ D]9]T\\ ?sFwDo?nC]L]?]L];" + "]T]7]T]=] Hj ?] 4]S]8d/]T]T]N^R_R\\ O](] =u Se =]0]J]7].^(]?]O]+]?^K]7]7]L]]K]A].]K]@p?]0]K]?]L]?].].a2].]M]M]N]L]@]J]@]K]A]K]?]-f8].]L]>^S^" + ":]U]V]U]?^4]S]4^4`0]$`<^Si O[O\\O\\H]N^=]M^@^S`<]J]=c7]L]<]S]8^U]:]L]@]O]O]J]S]S]@]P]>]S]S]@" + "]J]B]J]9]6]J]?]M]7]6]/^E^H]P]P]G]H]A]S]S]E]C]Iz<]]M]>]M]>]M]>]M]>^O^=]O]?]-].].]" + ".].].].].]-]E]G]Q]T]D]C]H]C]H]C]H]C]H]C]<`=]S]M]G]H]C]H]C]H]C]H];]6]M^?]R`;l=l=l=l=l=l=~Q]" + ".pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]S]T]@]L]?]L]?]L]?]L]=]S]:]K]>]S] M]P]P\\G]" + "C\\G]ScS\\G]S^N\\G]P]P\\B]0]S] D]7\\T[ >sFwCn?mB]L]?]L];]T]7]T]=] Hi >] 4]S]7[Xa1]T^T^O]" + "P_T] O](] =u Se =]0]J]7]/^'^A]N]+]?^K]7]8^L^]K]A].]K]@p" + "?]0]K]?]L]?].].b3].]M]M]N]L]@]J]@]K]A]K]?]+e9].]L]=]S]9]V]T]V]@_4]S]5_4b2]&b<\\Nd M[O]P\\H]N" + "^=]L]@]Q_<]J]?e7]L];]T]8]T]:]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?]M]8^6].]E]G]P]Q^G]H]A" + "^T]T^E]C]Iz<]]M]>]M]>]M]>]M]>]M]>^O]?]-].].].].].].].]-]E]G]Q^U]D]C]H]C]H]C]H]C]" + "H]C]=b>]T]L]G]H]C]H]C]H]C]H];]6]M]>]Qa>`P]>`P]>`P]>`P]>`P]>`P]>`PoQ].pApApAp@].].].]/]J]@]" + "L]@]J]A]J]A]J]A]J]A]J]?tG]T]S]@]L]?]L]?]L]?]L]=]S]:]K]>]S] L\\P]P\\F\\C\\F\\T^W^T\\F\\T^M\\F\\C\\B]" + "0]S] E^7]U[ >sFwBl=kA]L]?]L]<^T^8^V^=] Ij >] ]K]A].]K]@],]0]K]?]L]?].].c4].]M]M]N]" + "L]@]J]@]K]A]K]?](d;].]L]=]S]9^W]T]W^@`5^U^5^/_3]'_8ZJ` K[O]P\\H]N^=]L]@]P];]J]@_0]L];]U^9^T" + "^;]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]@^M^:^5].]E]F]Q]Q]F]H]@^U]U^C]E]G_\"]\"_BZT]TZB_F_;" + "] B]1]R[3]1\\L\\?o I_S] A[U]F[ V]T] W] N[S\\R]R[ S] ]L]6\\U\\ ']T]/\\O\\V\\@\\H\\A\\O\\V\\M_0o@o@o" + "@o@o?m>l>].].].].].].].].]-]F^G]P]U]C]E]F]E]F]E]F]E]F]E]=d?^V]L]F]H]C]H]C]H]C]H];]6]N^>]O`" + "?]M]>]M]>]M]>]M]>]M]>]M]>]M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U]R]@]L]?]L]?" + "]L]?]L]=^U^:]K]>^U^ L\\P]Q]F\\D]F\\U^U^V]F\\U^M]F\\D]B\\/^U^ OuD]V[ =sFwBk;i@]L]?]L]<]R]7]V];]" + " F^ Nu=[T^3]S]R]O]N_V\\ N](] 1] ].]L]6]1_%]Aq0]>]K]8]7]J]/] Md:u:d>]3\\R\\K\\S\\Po@]" + "J]A].]F]E].].]E]F]H]C].].]S^9].]RaR]H]P^V]C]E]F].]E]F]M],]8]6]H]>]U^8]W^Q^W]H^U^4]2^3]+],]" + " R^M]>]K]A].]K]@],]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]$`;].]L]=^U^8]W]T]W]@b5]U]5" + "^,]3]'] J\\Q_Q[G]N^=]L]A]O];]J]@].]L];]U]8]R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];" + "^4].^G^F]Q]Q]F]H]?_W]W_B]E]F_#]#_B\\U]U\\B_H_A\\U]U[ H]1]R[3]1]N]?o H`V] @[T]G[ U]T] X] N[S\\Q" + "]S[ S] ]L]6\\U\\ (]T]/]P\\U\\A]I]B]P\\U\\M^/o@o@o@o@o@o@m>].].].].].].].].]-]F]F]P^V]C]E]F]" + "E]F]E]F]E]F]E]>_X_?]W^L]F]H]C]H]C]H]C]H];]6]P_=]M^@^M]?^M]?^M]?^M]?^M]?^M]?^M]?].].].].]-]" + ".].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U\\Q]@]L]?]L]?]L]?]L]<]U]9]K]=]U] K]Q]Q]F]E]F]W^S^W]F" + "]W^L]F]E]B\\.]U] NuC\\V[ =eXZXdFgXhAi9h@]L]?]L]<]R]7]V];] E] Nu=[S]3\\R]R]O]M_X\\ M](" + "] 1] ].]L]6]2_$]Aq0]>]K]8]7]J]/] Ke=u=e<]3\\R\\K\\S\\Po@]J]A].]F]E].].]E]F]H]C].].]R^:].]Ra" + "R]H]O^W]C]E]F].]E]F]M^-]8]6]H]>]U]7]W]O]W]I^S^5]3^2]+],] R]L]>]K]A].]K]@],]0]K]?]L]?].]." + "]W_6].]M]M]N]L]@]J]@]K]A]K]?]\"_<].]L]<]U]7]W]T]W]Ac5^W^6^+^4](] H[R\\X]S\\G]N^=]L]A]O];]J]A" + "^.]L]:]W^9^R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];^4]-]G]D]R]R]E]H]>kA]E]E_$]$_B^V" + "]V^B_J_A^V]V] I]1]R[3]0\\N\\>o G`X] ?\\U_Q[T\\ T]T] ] N\\T\\Q]T\\ S] ]L]6\\U\\ )]T].\\P\\T\\A\\I]A" + "\\P\\T\\N^.o@o@o@o@o@o@m>].].].].].].].].]-]F]F]O^W]C]E]F]E]F]E]F]E]F]E]?_V_@]W]K]F]H]C]H]C]H" + "]C]H];]6k<]L^A]L]?]L]?]L]?]L]?]L]?]L]?]L]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]" + "V\\P]@]L]?]L]?]L]?]L]<^W^9]K]=^W^ J]R]R]D]G]D]W\\Q\\W]D]W\\L]D]G]A\\.^V] NuC]W[ ]K]9]6]J]/] H" + "e@u@e H\\R]M]T]Q^J]A]J]@]/]G^E].]-]F]F]H]C].].]Q^;].]Q_Q]H]N]W]B]G]E]-]G^F]L]-]8]6]I^>^W^7]" + "W]O]W]I^R^6]4^1]+],] R]M^>^M^@]/^M^?]-]0^M^?]L]?].].]V_7].]M]M]N]L]@^L]?^M^A^M^?] ]<].]L" + "]<]U]7]X]R]X]B^W^5]W]6^)]4](] H\\T]W]U\\F]O_=]L]A]P^;^L^A]-]L]:]W]8]P]<]L]@]O]O]J^T]T]?]P]>" + "]S]S]@^L]A^L]8]5]L]@^J]=^3]-^I^D^S]S^E]H]]G]C_%]%_A_W]W_A_L_@_W]W_ J]0]S[3]0]P]5]4],b =" + "[ThT[ R]T]!] M[T\\P]U[ R] ]L]6\\U\\ *]T].]P[S\\B]J]A]P[S\\N].^J]B^J]B^J]B^J]B^J]B^K^A]M]=]" + "/].].].].].].].]-]G^F]N]W]B]G]D]G]D]G]D]G]D]G]?_T_AbK]E]I^C]I^C]I^C]I^;]6j;]K]A]M^?]M^?]M^" + "?]M^?]M^?]M^?]M_?].].].].].].].].]/]J]@]L]@^L]@^L]@^L]@^L]@^L] J^X]Q]?]L]?]L]?]L]?]L];]W]8" + "^M^<]W] I]R]S]C]H]C]VZOZW]C]VZL]C]H]@\\-]W] MuC]X[ ;cWZWbDeWZXe>e6e>]L]?]L]=]P]8^X^:] " + " F^ H\\R\\5[S]5]Q]R]O^L` K]*] 0] !^.]L]6]4_\"]2],^>^M]8]6]J]0] DeCuCe E]R\\M]T\\P]I]A]J]@" + "]/]G]D].]-]F]F]H]C].].]P^<].]Q_Q]H]N^X]B]G]E]-]G]E]L^.]8]5]J]<]W]6^X]O]X^J^Q^6]5^0]+^-] " + "R]M^>^M]?].]M^?]-]/]M^?]L]?].].]U_8].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^<^W^6aRbB^V^6]W]7^(]4]" + "(] GcUcE]P_=]L]A]P]9]L]@]-]L]:^X]9^P]<]M^@]P^O]I]T]T]?]P]>]S]S]@^L]@]L]8]5]M]?]I]>^2],]I]" + "B_U]U_D]H]:c<]G]B_&]&_?_X]X_?_N_>_X]X_ I]0]S[3]0_T_5]4]+` ;[SfU[ P^U^#] L[U\\P]V[ Q] ]M^" + "6\\U\\ ,^U^-\\P\\S\\B\\J]@\\P\\S\\N].]I]B]I]B]I]B]I]B]I]B]I]B^M]=]/].].].].].].].]-]G]E]N^X]B]G]D" + "]G]D]G]D]G]D]G]@_R_A`J]D]J]A]J]A]J]A]J]:]6g8]K]A]M^?]M^?]M^?]M^?]M^?]M^?]M_?].].].].].].]." + "].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;aP]?]M^?]M^?]M^?]M^;]W]8^M];]W] H]S]T^B]J^B]J^B]J^B]J^@" + "\\-]W] G^1_ :aW[V`BcW[Wc]N]<]P]7]X]8] F]KZ X]S]5[S]5\\P]R]N]K_ K]*] 0] !]" + ",]N]5]5_\"]1],]<]M]9^6^L^0] Ad Nd A\\R]O^U\\P^I^B]K^?]H[C]H^D].],]G]F]H]C].].]O^=].]P^Q]H]M]" + "X]A]I]D],]I^E]K]AZH^8]5]J]<]W]5bObJ^O^7]6_0]*]-] R]M^>^M]?^/]M^?^.]/]M^?]L]?].].]T_9].]M" + "]M]N]L]?]L]?^M]?]M^?] ]<].]M^;]W]5aRaB^U^6c8_(]4](] FaSaD]P_=]M]@]P]9]L]@]-]L]9b9]O^=^N^?" + "\\P_Q]H]T]T]?]P]=]T]T]?^L]@]L]8]4]N]@^I^?]1],^K^A`W]W`C]H]7]8]I]@^&]&^=i=^N^^P^=^P]7]X]8_ H^M[ F] 6" + "]S]>ZQ[T^6]P]S^N^K^ K]*] 0]:] 8]0],]O^5]6_2ZI]1]-^<^O^9]4]L]0]<].] Uc Pc1]2\\Q^S`W^P]G]B]K]" + ">^J\\C]I^C].],^H]F]H]C].].]N^>].]C]H]MbA^K^D],^K^D]K^B[I]7]5^L^" + "_O]=].]O_>].].]O_?]L]?].].]S_:].]M]M]N]L]>]N]>_O]=]O_?] ]<]-]O_;]X^5aRaC^S^6a8_']4](] D]P" + "^B^Ra>^N]@]Q]7]N]?^.]L]9a8]N]=^N^?]Q_Q]G]U]U]>]P]=]T]T]?_N]>]N]7]4^P^@]G]@^1]+^M^?mB]H]7]8" + "^K^?\\%]%\\;g;\\L\\:g G]/]T[3]2n7]4]'^ <\\F\\ M\\S\\ J\\F\\ L^N^6\\U\\ ,\\S\\-]OhG]K]@]OhQ]LZ=]G]" + "D]G]D]G]D]G]D]G]D]G]D^L]<^J\\C].].].].].].].]-]J_D]MbA^K^B^K^B^K^B^K^B^K^A_N_B^K]B^L^A^L^A^" + "L^A^L^:]6].]K]A^O_?^O_?^O_?^O_?^O_?^O_?^Oa?].].].].]/].].].]-]N]>]L]>]N]=]N]=]N]=]N]=]N]2^" + ";_O]=]O_>]O_>]O_>]O_:a7_O]9a E^P_>^P_>^P_>^P_>^P_>\\,a H^.] /[5]T[S\\8a1`<]L]=^R^<]O^8b7_ " + " H^O\\ F] 6\\R\\=[R[U^5\\N]T]L^M` L]*] 0]:] 8]1^+]P]4]7_1[L_1]ZM];].] R` P" + "`.]2]QfXaN]G]B]L^=^L]C]K_B].]+_J]F]H]C].].]M^?].]C]H]La@^M^C]+^M^C]J]B]L^7]4^N^:a4aMaK^M^8" + "]7^.]*^.] Q]P`>`Q^=^NZ;^Q`>_LZ>].^Q`?]L]?].].]Q^;].]M]M]N]L]>^P^>`Q^=^Q`?]/ZL];]-^Q`:a4`" + "P`D^Q^7a8^&]4](] S]Sb>_P^@]R^7^P^>^MZ<]L]9a9]M]=_P`XZB]Q_Q]G^V]V^>]P]=^U]U^?`P^>^P^6]4]Q" + "^?]G]A^0]*^O^]P`>]P`>]P`>]P`>]P`>]P]X^LZN^NZ;_LZ>_LZ>_" + "LZ>_LZ?].].].]-^P^>]L]>^P^=^P^=^P^=^P^=^P^2^:^P^=^Q`>^Q`>^Q`>^Q`:a7`Q^9a Dk],a " + "H]-] /[,[._0_;]L]=j<]N]7`5a J_S^ F] 6\\R\\=^U[W_5]N^V^K_Rd L],] /]:] 8]1])^T^3]8_0^Q`0" + "]<]Q_8^S^8^3_R_=]R^:].] O] P]+]1\\PdW`N^G^C]N_;`R`C]NaA].]*`O`F]H]C].].]L^@].]C]H]La?`S`B]*" + "`S`B]J]B`Q_6]3_R_9a4aMaL^K^9]8^-])].] Q_Tb>aS^;_R\\:^Sa=`Q]>]-^Sa?]L]?].].]P^<].]M]M]N]L]" + "=_T_=aS^;^Sa?]/^R_:]-^Sa:a3_P_C^P^7_8^%]4](] S_V^X^?aS^>]T^5_T_=`R]<]L]8_8]M^>`SdA]SaS]E" + "^W]W^=]P^=_W]W_>]X]T_<_T_5^4^T^?^G^C^/])^Q^8c=]H]7]6`S` ?] ;c >c E]._W[V\\9]4^J^9]4]%] ;]L]" + " IZQZ H]L] !u ,`Sd9\\U\\ ,ZQZ,]E\\E]L]?]E\\M_S^>^G^F^G^F^G^F^G^F^G^F^G^F^K]:`R`C].].].].]." + "].].]-]ObB]La?`S`>`S`>`S`>`S`>`S`?]J]CcS`?_R_=_R_=_R_=_R_8]6].]V[R^?_Tb>_Tb>_Tb>_Tb>_Tb>_T" + "b>_T^V_Q]M_R\\:`Q]=`Q]=`Q]=`Q]?].].].],_T_=]L]=_T_;_T_;_T_;_T_;_T_1^:`T_;^Sa=^Sa=^Sa=^Sa9_6" + "aS^7_ Bi:i:i:i:i=]+` I],] /[,[-].]:]L]]C]H]K`>kA])kA]J^Cm5" + "]2j7_2`M`K^J]9]8tC])].] PgX]>]Xf9h9fX]],fX]?]L]?].].]O^=].]M]M]N]L]qA^U]W]U^Di<]O`?k=]Xg:h3a7f>uCn?]/eSe;]:" + "]H]7]5k >] :a n?\\H\\8]4]%] 9^R^ *^R^ Xu ,q9\\U\\ /]D\\F]LfH]D\\Li>]E]F]E]F]E]F]E" + "]F]E]F]E]F]JnIkBn?n?n?n?].].].]-n@]K`>ki-]]C]H]K`]Wd" + "6f8dW]:i>]+dW]?]L]?].].]N^>].]M]M]N]L];f;]Wd7dW]?]/i7c3dV]9_2_P_E^M^8_8m4]4](] QdV`B]Xe;" + "d1f8h<]L]8_9]K]>]XdW_@eWeBg;]O`=g;]Vd8f1`6d=uCn?]/eSe;]:]H]7]3g <] 9_ :_ C]+f>n>ZFZ7]4]%] " + "7f &f Vu ,]XdW_9\\U\\ /\\C\\F\\KfH\\C\\Kg=]E]F]E]F]E]F]E]F]E]F]E]F]JnHh@n?n?n?n?].].].]-l>" + "]K`]C]H]J_9a<]$d?]I^?c0].b3_2_K_M^G^;]8tC](]/] M`T]>]U`2b4`U]7c;])`U]?]L]?].].]M^?].]M]" + "M]N]L]8`8]U`3`U]?],c2a0_T]9_2^N^F^K^8]7m4]4](] O`R^B]Va8b-`3d:]L]7]9^J]?]V`T]>cUc?c9]N_:" + "a8]T`3`-_4`X IX *W FW " + " " + " " + " " + " " + " HX W 4Z 3VCT X W 4Z " + " HX W 4Z 'VCT ;X W 3Y 2UCT KX W 3Y 0W " + " " + " " + " " + " " + " @W !W 4\\ 5YET ?XHX 8] >W !W 4\\ 7XGX KW !W 4\\ 7XHX " + " +YET :W !W 3[ 5ZFT ?XGX EW !W 3[ 7XGX 5W " + " " + " " + " " + " " + " >W \"V 3\\ 7]HU ?XHX 9` ?W \"V 3\\ 7XGX JW \"V 3\\ 7XHX -]HU" + " 9W \"V 3] 7]HT ?XGX DW \"V 3] 8XGX 5V " + " " + " " + " " + " " + " W $V 3VNV 8XGX IW $V 3VNV 8XHX -_KV" + " 8W $V 2] 7_KU ?XGX CW $V 2] 8XGX 6V " + " " + " " + " " + " " + " :W &W 4VLV :j >XHX :VJV >W &W 4VLV 9XGX HW &W 4VLV 9XHX .j 6" + "W &W 3VMV 9i >XGX BW &W 3VMV 9XGX 7W MW " + " " + " " + " " + " " + " CV 'W 4VJV ;j >XHX ;UGV >V 'W 4VJV :XGX GV 'W 4VJV :XHX .j" + " 5V 'W 3VKV :i >XGX AV 'W 3VKV :XGX 8W N[ " + " " + " " + " " + " " + " DV )W 4VHU TEY ;XHX V ,V 2UEU TCU :XGX =U -V 2UCU =XGX ;V NV" + "IV \"W " + " " + " " + " " + " JU /V 3VBV ETBT :U /" + "V 3VBV FU /V 3VBV (U /V 2UAU DU /V 2UAU @V NVGV " + " $X " + " " + " *X " + " " + " JX GTBT " + " MX GX 7V :UEU DX GX 7V JX GX 7W 4X GX 6V " + " GX GX 5V (X &X " + " " + " )X 8V " + " " + " ;X FTBT " + " LX IX 7X W E\\ AW ,W ,W ,W ,W HY GV +Y " + " 4Z NX @X " + " %W DUDU " + " =Y 7W KW 6Z 4XDT BTAT BW KW 6Z IW KW 6[ ,Y )XDT AW KW 5Z 4XDT " + " KW KW 4Z ,W BW 8V (S " + " W H_ AW ,W ,W ,W ,W " + " L] GV +] ;a #[ F^ " + " 8XGX +W BTEU " + " *R 9a :W MW 6\\ 6ZET ?XHX W Ja AW ,W ,W ,W ,W N_ GV +_ " + "?e 8] J] Jb 8[ <[ $Y FY 7XGX " + "=Z Di 5W 8Z .Y !W FW *Y 4W)V*W)V-Y(V W $a MY EW 5W >W Kb AW ,W ,W" + " ,W ,W !a GV +a Ch =f ^ Mf 2Z @" + "x Mx a" + " 5a &W 0g #\\ -_ <\\*V.\\*V0a-V\"X )Z /Z /Z /Z /Z 4WJV 1~U+d Kx Mx Mx Mx MX -X -X -X ,j" + " @[3X Dc 8c 8c 8c 8c W \"W 4VNV 8]HU ?XHX BW \"W 3VNV 8XHX 2W ?W &XHX " + " ^ K~\\ >S 3Q +[ @[;[ ;Q ;e HX 2VFV #VBV FS 6`" + "1V#g GV !V 3V !T 7W 0d :` ;j ?k -[ Dq :g Ky Df ;d $f 1Z @o 5j Np Ex Mt " + ":m\"X/X'X -X -X3Z%X -]0]0\\4X Gi Lm 4i Ln ;m#~W$X/X-X(X-X4Y4XCY1Y-Y.Y&~S%a >W $a N[ EV " + "5W >W Lc AW ,W ,W ,W ,W \"b GV +a Dk Aj \"_" + " h 3Z @x Mx ?i 6X C~Q)X?X?X Ni 6V /V /V DX &f #W0W e >XGX %c#" + "e +b\"i 9_ Be 9d 'V 3k %^ /c @^*V0^*V2d.V\"X )Z /Z /Z /Z /Z 3b 1~U.j Nx Mx Mx" + " Mx MX -X -X -X ,p F\\4X Gi >i >i >i >i BiEV.X/X'X/X'X/X'X/X.Y.Y#X 'j ;V \"V 5VLV :_IT >XH" + "X V \"V 5VLV 9XGX IV \"V 4VMV 9XGX ,ZHY A_IT XHX AV \"V 3VLV 9" + "XHX 2V >W &XHX !_ K~[ >T 4R -_ D_?_ >S =t Fh " + " IX 2VFV #VBV FS 7c4V#i HV \"W 3V !T 7V 0f @e >o Co 0\\ Dq W Md AW ,W ,W ,W ,W HW 1b GV +b " + " Fm Dm #` \"j 4Z @x Mx Am 8X C~Q)X?X?X!m 9X 0V 0X EX 'h" + " $W0W \"h ?XGX 'g%g 0h%i :a Cf :f *V 4m %^ 0e A^+V/^+V1f1V!X )Z /Z /Z /Z /" + "Z 2` 1~V0o\"x Mx Mx Mx MX -X -X -X ,t J\\4X Im Bm Bm Bm Bm FmHV-X/X'X/X'X/X'X/X-X.X\"X (l ;" + "V $V 4UJU :ULXLU >XHX XHX @V $V 2UJU 9XHX 3V =W &XHX !` K~Z >T 4S /a FaAa @T " + " @w Hl KX 2VFV $WCV ES 8e5V$j HV \"V 1V \"T 7V 2j Eh ?q Dp 1\\ Dq >" + "l Ly Hn Bj +l %e E\\ At >s$v Kx Mt >u&X/X'X -X -X5Z#X -^2^0]5X Jo q ;o r Br%~W$X/X" + "-X(X,X6[6XAY3Y+Y0Y%~S%W 3V IW !_ FW 7W >W Md AW ,W ,W ,W ,W HW " + " 2[ ?V #[ Hn En #` #l 6\\ Ax Mx Cp 9X C~Q)X?X?X\"o " + " ;Z 1V 1Z FX KS 0i #W2W LV ,i ?XGX *l'h 3l'i ;c Dg ;g ,W 6o %^ 1g B" + "^,V.^,V0g3V X *\\ 1\\ 1\\ 1\\ 1\\ 2^ 0~V2s$x Mx Mx Mx MX -X -X -X ,v L]5X Jo Do Do Do Do HpKW" + "-X/X'X/X'X/X'X/X-Y0Y\"X )n XHX ;UEU XHX @W &W 3VJV :XHX 4W =W &XHX 1\\ 1\\ 1\\ 1\\ 1\\ =XMV K~Y " + " =S 4U 1c IdCc AU Dz In LX 2VFV $VBV ES 9g7V$k HV #W 1W #T " + " 8W 3l Fh ?r Eq 3] Dq ?m Ly Ip Em -n )k H\\ Au Av%x Mx Mt ?x(X/X'X -X -X6Z\"X -" + "^2^0]5X Ls\"s ?s\"s Et%~W$X/X,X*X+X6[6X@Y5Y)Y2Y$~S%W 3W JW \"a FW 8W >W NZ 6W ,W " + ",W ,W ,W HW 2X \\ 2V 2\\ GX KS 1j #W2W LV -j ?XGX +ZEZ)VGY " + "5ZDZ)i T 5V 2e KfEe CW G| " + " Jp MX 2VFV $VBV ES 9XIX8V$l HV #V /V #T 8V 3n Gh ?s Fr 5^ Dq @n Lx Ir" + " Go .o -q L^ Bv Cx&z x Mt A{)X/X'X -X -X7Z!X -^2^0^6X Mu#t Au#t Gu%~W$X/X,X*X+X6[" + "6X?X5X'X2X#~S%W 2V JW #c FW 9W >W NX 4W ,W ,W ,W ,W HW " + " 2W ;V NW IZCY Hp JY &ZDZ 9^ Bx Mx Eu W *W 2UFU ;XHX 6W ;W &XHX 7h =h" + " =h =h =h DWJV K~X >T 5W 4g MgFg EY J~ K]FZ MX 2VFV $VBV " + "ES :XGX9V%\\GX HV $W /W 3PATAP GV 3[H[ Gh ?]F] GZE^ 6^ Dq A]FX Lx I\\F\\ G\\G[ " + " /[H] 0u N^ Bw E_D^&{!x Mt B`C_)X/X'X -X -X8Z X -_4_0_7X N^E^$u C^E^$u H^E\\%~W$X/X,Y,Y*W7" + "]8X>Y7Y'Y4Y#~S%W 2V JW $e FV 9W >W NW 3W ,W ,W ,W ,W HW " + " 2W ;V NW IY@X >X 4[AV IX &X@X 9^ Bx Mx F^E^ =X C~Q)X?X?X" + "&^E^ B` 4V 4` IX KS 3\\GW \"W4W KV .YBT ?XGX .V7V,P=W :W8W /VEV 3V +V /V " + " 7eGU KU 3WCW ;U-V$U-V LV5V NX +^ 3^ 3^ 3^ 3^ 3^ 1~W6_D^&x Mx Mx Mx MX -X -X -X ,{\"" + "_7X N^E^ L^E^ L^E^ L^E^ L^E^ !^Ed*X/X'X/X'X/X'X/X+Y4Y X +Y?X ;V *V 4UDU >TEZ TEZ T 5Y 5g MhHi G[ M~Q L\\AW M" + "X 2VFV $VCV DS :WEW:V%ZAU HV $V -V 3RCTCR HW 4ZDZ H\\LX ?Y?[ HV>\\ 8_ DX )[?T -Y J[B" + "[ I[CZ 0WAZ 2x ^ BX>^ G]=Z&X=b#X -X '];[)X/X'X -X -X:[ NX -_4_0_7X \\?\\%X@^ E\\?\\%X" + "?] J[=X =X W X 3W 4W ,W " + " HW 3X ;V NX KY?X Ca 9Y:R HX (X>X :VNV " + "BZ /X '\\?\\ A^ FX0X)X?X?X'\\?\\ Db 5V 5b JX KS 3ZBT !W6W JV .X?R 4V4U HV ;V" + "4V 1VCV 4V *U 0V 7fGU KU 4WAW TDX ;a 6V ,V 4UBU GV ,V 3UCU 0` 6TDX 4V ,V 2UDU >TDX >V ,V 1UDU " + ":V 9W (o Do Do Do Do GWIU J~V >T 6Z 6i jIj I\\ N~R M[=" + "U MX 2VFV %VBV H] AWCW;V%Y=R HV %W -V 4UETEU IV 4ZBZ IWGX ?V;[ IS9Z 9VNX DX *Z;R" + " -X JZ>Y JZ?Y 1U>Z 5`C_#` CX;[ H[7W&X9_$X -X (\\6X)X/X'X -X -X;[ MX -_4_0`8X![;[&X" + "=[ F[;[&X<[ LZ8U =X W W 2W 4" + "W ,W HW 3W :V MW KX=W Cc ;X7P HX (" + "WR !X8X JV /X

W W " + " 2W 4W ,W HW 3W :V MW KWU.U 4VAV &V 5U *U 2V 6gGU KU 5W?W =U/V\"U/V IU7V LX ,WNW 5WNW 5WNW 5" + "WNW 5WNW 5WNW 4XHX H[4U&X -X -X -X -X -X -X -X ,X6]&`8X\"Z7Z#Z7Z#Z7Z#Z7Z#Z7Z 'Z8['X/X'X/X'" + "X/X'X/X)Y8Y MX ,W:W 9V 0V 3U@U ?[ 1V 0V 3U@V GV 0V 3U?U 8h 1V 0V 2U@U " + " CV 0V 1U@U >V 7W *`L` I`L` I`L` I`L` I`L` JV =X,X >T 6] 9k\"lKl K_ " + " #\\ 'Y8S MX 2VFV %VBV Nk IVAV=V$X 1V %V +V 6YHTHY -V EW 5Y>Y :X ?R5" + "Z .Y ;VMX DX +Y DX IYW W 2W 4W ,W HW 3W :V MW KW;W De =W " + " -X *W:W V$X 1V &W +W 5XITIX +V EV 4X[ JX -XNW8WNX0a9X#Y" + "3Y(X9Y JY3Y(X9Y NX LX W W 2W " + " 4W ,W HW 3W :V MW LX;W Df >W ,W " + " +W8W >WLW @Y 2X +Z3Z!t\"X0X)X?X?X*Y3Y Kj 9V 9j AS 5X 8W:W HV /W #T)T KV " + " @T(T 6U?U &V 5T +V AhGU KU 5V=V =U0V!U0V JV7V WLW 7WLW 7WLW 7WLW 7WLW 7XNX " + "6XGX IY.R&X -X -X -X -X -X -X -X ,X2Z'a9X#Y3Y%Y3Y%Y3Y%Y3Y%Y3Y )Y3Z)X/X'X/X'X/X'X/X'X:X Ki" + " >W8V *XHZ FW ,ZW W 2W 4W ,W HW" + " 3W :V MW LW:W Dg ?W ,X ,W8W >WLW ?Y 3X +Y1Y\"v#X" + "0X)X?X?X+Y1Y MYNVNY :V :YNVNY BS 5X 8XU1V U1V KW7V NWLW 7WLW 7WLW 7WLW 7WLW 7WLW 6XGX JY,Q&X -X " + "-X -X -X -X -X -X ,X1Z(XNX:X$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y P)P$Y3[)X/X'X/X'X/X'X/X'YVKX DX -X BX IX8X NX7W KP 1P =X Y *Z W 0W MW +ZAZ 0W >W W 2W 4W ,W HW " + " 3W :V MW LW:W DSF[ @X -X -X8W ?WJW ?Y 4X ,Y/Y%z%X0X)" + "X?X?X,Y/Y YMVMY ;V ;YMVMY CS 5X 5P*Q JWU2V NU2V$_7V NXLX 9XLX 9XLX 9XLX 9XLX 8WLW 6XGX KY*P&X -X" + " -X -X -X -X -X -X ,X0Z)XNX:X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y\"R+R&Y3]*X/X'X/X'X/X'X/X&Y>Y Jp EW:Y " + " +R@Y 7Q 2W .XEVFY\"X5Y\"X5Y\"X5Y\"X5Y N" + "V ;X/X 0V 5T 8c ^ AW4W ?Z >W6W KY \"Y 0X 2VFV &VCW#[LSKZ K" + "V?V@V\"W 0V 'W )W 1XNTNX &V FW 6Y:Y X *Z NW 0W MW ,Z?Z 1W >W W 2W 4W ,W H" + "W 3W :V MW LW:W DPAY ?Y .W -W6W @WJW >Y 5X ,X-X&" + "_MXM_&X0X)X?X?X,Y/Y !YLVLY W FV /X 'TCfFT2i CUGfB" + "T 9U?U &V 7U 5] >iGU KU 6V;V >U2V NU2V$]5V NWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX" + " KY /X -X -X -X -X -X -X -X ,X/Y)XMX;X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y#T-T'Y3]*X/X'X/X'X/X'X/X%X>X Ir " + "GW=\\ GY 9S 3W /XDVDX$X2X$X2X$X" + "2X$X2X V ;X0X 0X 7T 8d X$X-WJW EX6X Y .X.Y)X -X -Y .X/X'X -X -XBZ EX -XLV:VLX0XMX;X&Y-Y+X7X NY-Y+X7X!X KX Z W FV " + ".X (TDgFT3j CTFhDT 9U?U &V 8U 4\\ =iGU KU 6V;V >U3V MU3V#\\5V MWJW 9WJW" + " 9WJW 9WJW 9WJW 9WJW 8XFX LY .X -X -X -X -X -X -X -X ,X.Y*XMX;X&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y%V/V)Y3" + "_+X/X'X/X'X/X'X/X%Y@Y Is HW?^ ?Z /Z /Z /Z /Z /Z /Z6Y NZ 0Z /Z /Z /Z 8Y 1Y 3Z /Z /Z" + " /Z /Z 3ZCV 5WDX DXCVCW%X0W%X0W%X0W%X0W V :X1X 0X 7T 9f =k#~`\"h Cf " + "EW4W @\\ ?X8X LX !Y /X 2VFV 'VBV#XHSET KV?VAV!W 0V (W 'W .` \"V GW 5X" + "8X W\"W.XJX FX6X X -X.Y)X -X -X -X/X'X -X" + " -XCZ DX -XLV:VLX0XLX^4WG_ 9` @WG^ 9^GW MWG\\ ;f Gm ^BV\"W:W 3X ?^ 0e AWG_ KV.X ?X Z 7X -X+X)\\HXH\\(X0X)X?X?X-X+X $YJVJY >V >YJVJY Ma =X 7V0V JW@W E" + "V .Y *TEiET5k DTEiDT :VAV &V 9U 3_ ;W6W NiGU KU 6V;V >U3V MU3V#_8V NXJX" + " ;XJX ;XJX ;XJX ;XJX ;XJX :XEX LX -X -X -X -X -X -X -X -X ,X.Y*XLXa'b 7` 5` 5` 5` AW ,W ,W ,W DY EW" + "G_ 9` 5` 5` 5` 5` (Z <`GV W6W MW6W MW6W MW6W#W1X NWG^ HW1X NWBVBW&W.W&WJP:PJW&W4PJW&W." + "W!V :X2X 0X 6S 8g >k#~`#j Fj GW4W @\\ >W8W LX X .X 2VFV 'VBV$XGSCR " + "KV?VBV X 1V (W 'W ,\\ V GW 5X8X f CWIb =bIW MWI^ =j Im U4V LU4V\"`:V GX /WHW ;WHW ;WHW ;WHW ;WHW ;WHW :XEX MY -X -X -X -X -X " + "-X -X -X ,X-Y+XKWf ;f ;f ;f ;f +Z >eJU NW6W MW6W MW6W MW6W\"W" + "2W MWIb IW2W NWAVAW(W,W(WJRU5V KU5V GXTKW)W4TKW" + ")W+W\"V 9X3X 2X 5T :k ?i\"~`$m Jn IW4W A^ ?X:X MW NY .X 2VFV 7~X2XFS" + " VIV>X2YIY DYFY +Z JW .V NW 1Y3Y 1n DWLh Bm ChLW Gk Ll 6hLW MWKg HW ,W ,W;Y JW " + ",WKfGg8WKg Cl FWLh ChLW MWK` @m Im Y =W6W JW-W&YJb }!WCWCW Hk Dx&{ W4W CWFW P JSCVAVDS :WEV " + "$V W6W NiGU KU 6V;V BP>P /U5V KU5V EW=V FX 0XHX =XHX =XHX =XHX =XHX =XHX W:X MW NX -X 2VFV 7~X2WES WKX0XJX>X(Y)X,X7X!Y)X,X7X!Y LX VIV>X1YKY BXFX +Z IW .W " + " W 2Y1Y 2o EWMj Dn DjMW Hn Nl 7jMW MWLi IW ,W ,WW6W NiGU KU 6V;V BQ" + "?Q 0U6V JU6V BU>V EX 0WFW =WFW =WFW =WFW =WFW =WFW X(Y)X.Y)X.Y)X.Y)X.Y)X%Z9Z*Y6WJX,X/X'X/X'X/X'X/X!XFX EX;Z LWDX ?o Do Do Do Do Do DoKn4n C" + "n Cn Cn Cn HW ,W ,W ,W %l HWLi En Cn Cn Cn Cn /Z Cs LW6W MW6W MW6W MW6W!W4W LWMj LW4W " + "W?V?V+W(V+WKXBXKV+W5XKV+W(V$W 8W4X 2X 5T ;n ?g!~_%p LZDZ JW4W A^ >W:W MW " + " MX -X 2VFV 7~X2WES VJX0XIW>X(X" + "'X-X7X!X'X-X7X!Y LX VIV>X1YKY AXHX +Z HW -V W 3Y/Y 3p FWMk Fo EkMW Io Nl 8" + "kMW MWMk JW ,W ,W=Y HW ,WMjJj:WMk Gp HWMk GkMW MWMb Bo Im \\>W0X=X LW5X u 6W :V " + " MW EkJV Wj Fn CWMk\"\\6X =Z >W6W KW+W)[Ke\"}!WCWCW Jo Hz&{ W4W DWDW ;Y ;X /X'X." + "YBXBY+X0X)X?X?X/X'X#T HV IT :V ;T3T :V CV +o BX 6ZM`MZ GXFX CV *\\ 3SFW,S:V>V 0R@R " + " KSBV@VDS 9e #V ?W \"V ?W6W NiGU KU 6V;V BR@R 1U6V JU6V BV?V EX 1XFX ?XFX ?XFX ?XFX" + " ?XFX ?XFW =XCX NX +X -X -X -X -X -X -X -X ,X+X,XIW>X(X'X/X'X/X'X/X'X/X'X%Z;Z)X5VHX-X/X'X/" + "X'X/X'X/X XHX DX:Y LWEX >p Ep Ep Ep Ep Ep EpMp6o Do Do Do Do HW ,W ,W ,W 'o IWMk Gp Ep Ep " + "Ep Ep 0Z Ds KW6W MW6W MW6W MW6W!W5X LWMk MW5X V>V?W,V'W,VKZDYKW,V5YKW,V'W%W 8X5W 2" + "X 4T ;o @g ~^%q NY@Y KW4W B` ?XX -XJW@WJX0XIX?X(X'X-X7X!X'X-X8Y Y MX " + "W/YMY @YJY +Y GW -V W 4X+X 4YE\\ FWNXG\\ H]EX F\\GXNW J\\F[ GW ,\\GXNW MWNXG[ JW ,W ,W?Z GW" + " ,WNXH[KXH[:WNXG[ H]H] IWNXG\\ I\\GXNW MWNXFQ C\\CW CW ,W6W!X6X NW?\\?W.X?X JW6W 1X 6W :V MW " + " 9X=X\"[IZKW W=Y /W @m H]DV CWNXG[\"\\6W =[ >W6W LW)W*ZJWKY\"}!WCWCW K\\H] J{&{ V3W DWDW :Y " + "XCX NX +X -X -X -X -X -X -X -X ,X+X,XIX?X(X'X/X'X/X'X/X'X/X'X$Z=Z(X6WH" + "X-X/X'X/X'X/X'X/X YJY DX9Y MWEW =YE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE]N\\G[7]EX E\\F[ F\\F[ F\\F[ " + "F\\F[ IW ,W ,W ,W (p IWNXG[ H]H] G]H] G]H] G]H] G]H] 1Z E]H^ JW6W MW6W MW6W MW6W W6W KWNXG\\" + " MW6W NV>V>V,V&V,VJZFYIV,V6YIV,V&V%W 7W6X 3X LR:T ;q @e N~^&s!Y>Y LW4W B` >WXJX +Z GW -W !W 5X)X 5U>" + "Z G_CZ I[>T FZC_ KZAZ HW -ZB_ M^BZ KW ,W ,W@Z FW ,^CZMVCZ;^BZ IZBZ I_CZ IZC_ M^ 5YY .W AXJa IZW2W EWDW 9Y =X /X'X/YAXAY,X0X)X?X?X/X'X%X JV KX Z FU>Z " + "FU>Z FU>Z FU>Z FU>Z FU>eBZ9[>T FZAZ HZAZ HZAZ HZAZ JW ,W ,W ,W )r J^BZ IZBZ GZBZ GZBZ GZBZ" + " GZBZ 1Z EZB[ JW6W MW6W MW6W MW6W W6W K_CZ MW6W V=V>V-V%V-VHZHYHV-V6YHV-V%V%W 7X7X " + " 4X NU:T WX !Y 0Y BVDX Dk CXJc -X BX>X LX5Y MX -X Ee Le 3Z ?U=bKUC" + "U6XDX IX9Y X +X+X+X -X /X +X/X'X -X -XL[ Y J]?Y KY?] M] 4X8P CW ,W6W X8X MW?\\?W-XAX IW7X 3Y 5W :V MW =_C_(YBXLV NW" + "?Z -W CXC\\ KY ,]@Y LW8X >] ?W6W LW)W,YHWHY MW=W JWCWCW MY>Y L[B[ ;W >W2W FWBW 9Y >X 0X%X0X" + "@X@X,X0X)X?X?X/X'X&Y JV KY =V >Y7Y =V CV .[HSFR BX 3t BWHW AV .WN\\ 9SFV)S;V?W 3UCU " + " LSAV@VCS 7_ V BV LU ?W6W MhGU KU 5W?W AUCU 4U8V HU8V ?UAV CX 2XDX AXDX AXDX AX" + "DX AXDX AXDX @XBX NX +X -X -X -X -X -X -X -X ,X+X,XHX@X(X'X/X'X/X'X/X'X/X'X\"ZAZ&X8WFX-X/X'" + "X/X'X/X'X/X MXLX BX8X MWFW Y;Z:R GY=Y JY=Y JY=Y JY=Y KW" + " ,W ,W ,W *]E[ J]@Y JY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y JW6W MW6W MW6W MW6W W7X K]?Y NW7X " + " V=V=U-V$U-VGZJYFU-V7YFU-V$U%W 7X8X &~X/X:T =t @c L~\\'v\"W:W LW4W CXNX ?X>X MV " + " $x EX 2~X2WES :VDWEV FZ :W #W 7XKTKX )V IV 4X4X >X !X 0Y BWDX Dm FXKf " + "/Y AYBY KX5Y MX -X Gd ~X d 5Y ?V>dLUCU6WBW IX;Z Y +X+Y,X -X 0Y +X/X'X -X -XM[ ;X -XIWBWIX" + "0XGW@X)Y'Y.X8X!Y'Y.X9Y M] #X aEa)X@XNW NWA[ ,W DW?[ LX +[=X KW:X =] ?W6W MW'W-XGWGX MW=W JWCWC" + "W MXZ W2W FWBW 9Z ?X 0X%X0X@X@X,X0X(X@X@X/Y'Y(Y IV JY >V ?Y5Y >V CV .YFSDP B" + "X 2q @XJX AV /WK[ :SFV)S;V@X 4VDV LSAV@VCS 6\\ MV CV KU ?W6W MhGU KU 4V?V @V" + "DV 5U9V GU9V >UBV BX 2WBW AWBW AWBW AWBW AWBW AXDX @XBX Y +X -X -X -X -X -X -X -X ,X+Y-XGW" + "@X)Y'Y1Y'Y1Y'Y1Y'Y1Y'Y\"ZCZ&Y9WEY.X/X'X/X'X/X'X/X MYNY BX8Y NWFW X NW $w DX $VBV#XFS :WFXEV H] ;W #W 9XITIX" + " +V JW 4X4X >X \"Y 3[ BWCX Dn GXLi 1X ?ZFZ JY7Z MX -X Je M~X Me 9Y >U?gMUCV7WBW IX>\\" + " NX *X*X,X -X 0X *X/X'X -X -XNZ 9X -XHVBVHX0XGXAX)X%X.X9Y!X%X.X:Y La 'X _ @W6W MW'W.YGWFX NW=W JWCWCW NX:X NYW2W FWBW 8Z @X 0X%X0X@X@X,X0X(X@X@X" + "/X%X)Y HV IY ?V @Y3Y ?V CV /YES 6X 1\\H[ JcJc LV 0WI\\ =TFV)S;WAX 5WEW MTAVAWCS 3" + "W 4~W.W KV ?W6W LgGU KU 4WAW @WEW 6U9V GU9V ?VBV BX 2WBW AWBW AWBW AWBW AWBW AWBW A" + "XAX X *X -X -X -X -X -X -X -X ,X*X-XGXAX)X%X1X%X1X%X1X%X1X%X!ZEZ%X9WCX.X/X'X/X'X/X'X/X LXN" + "X AX7X NWFW !W ,W ,W ,W ,W ,W ,]:X=Y .X9X LX9X LX9X LX9X LW ,W ,W ,W +Z=X K[x A` J~\\(y%W8W MW4W CXMW >W>W MV $x DX $VCV\"XFS 9X" + "IXEV H_ X #Y ?g AVBX Do HXMk 3Y >l HX7Z MX -X Me J~X Je " + "=Y >V?hNUBU8XBX Ju MX *X*X,w Lq IX *~R'X -X -c 8X -XHVBVHX0XFWAX)X%X.X9Y!X%X.X;Z Ke ,X WNV MW" + "Ib +W EW;Y MW *Z;X KV:W =_ @W6W NW%W/XFWFX NW=W JWCWCW NW8X!Y:Y =W >| GW@W 8Y @X 0X%X1Y@X@" + "Y-X0X(X@X@X/XImIX*Y GV HY @V AY1Y @V CV /XDS 6X 0YDY JdLd LV 1WF[ >SFV'SW6W LgGU KU 3WCW ?XFX 7U:V FU:V >UBV AX 3XBX CXBX CXBX CXBX" + " CXBX CXBX BXAw?X *w Lw Lw Lw LX -X -X -X ,X*X-XFWAX)X%X1X%X1X%X1X%X1X%X ZGZ$X:WBX.X/X'X/X" + "'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8W=X -W7W LW7W LW7W LW7W LW ,W ,W ,W ,Y:X LZ;X M" + "Y:Y MY:Y MY:Y MY:Y MY:Y \"Y=\\ LW6W MW6W MW6W MW6W MW:W IZ9X NW:W NVV&V 4W:X %~X2TNVW \"W ;WFTFW -V JV 3X4X >X #Y ?f AWBX Dp IXNm 4X ` @W6W NW%W/WEWEW NW=W JWCWCW X8X!X8X =W >| GW@W 7Y AX " + "0X%X1X?X?X-X0X(X@X@X/XImIX+Y FV GY AV BY/Y AV DX 1XCS 6X 0W@X KdLd LV 1VCZ ?SFV'S;WE" + "[ 7XFX G~X .S@VBWAS @~W0W .P>W >W6W KfGU KU 3XEX >XFX 8U;V:W3U;VCZ9P>WCV:W/Y 3W@" + "W CW@W CW@W CW@W CW@W CXBX CX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XFXBX)X%X1X%X1X%X1X%X1X%X N" + "ZIZ#X:VAX.X/X'X/X'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8X?X -X7X NX7X NX7X NX7X MW ,W " + ",W ,W ,X9X LY9W MX8X MX8X MX8X MX8X MX8X \"X=] LW6W MW6W MW6W MW6W MW:W IZ9X NW:W NVLu" + "KU/VLuKU/VBaAU/V:YAU/V=X=U&V 4X;X %~X2RLW>T >{!z'~Z)}(W6W NW4W DXLX ?X@X MV " + " KX ,X %VBV!YHS 8eEV Ic ?W !W ;UETEU ,V KW 3X4X >X $Y >c ?WAX DWD^ JbG] " + "5X 9d DY9[ MX -X #d D~X Dd DY a AW6W NW%W0XEWEX W=W JWCWCW W6W!X8X " + "=W >| HX@X 7Y BX 0X%X1X?X?X-X0X(X@X@X/XImIX,Y EV FY BV CY-Y BV DX 1XCS 6X 1W>W KeNe LV" + " 1VB[ ASFV'S;YI] 9YGY F~X .S@VDX@S @~W1V ,TEZ >W6W JeGU IX +U 2YIY T ?|\"}(~X)~(W6W NW4W DXKW >" + "W@X MV KX ,X %VBV!ZIS 7cEV IYNZ8W 0W !W :RCTCR +V KW 3X4X >X %Y" + " =b >V@X DS=\\ K`C[ 6Y 8b BX9[ Nd A~X Ad HY W ,X8X8W=X8X X6X MY7X\"X7Y MX 0W )W ,W6W MXXMW AW6W NW%W0XEWDW W" + "=W JWCWCW!X6X#X6X >W >| HW>W 6Y CX 0X%X1X?X?X-X0X'XAXAX.XImIX-Y DV EY CV DY+Y CV DX 2X" + "BS 6X 1Vh =W6W JeGU IX 4g :g :" + "YFX DgEV:XhCV:X/X 3X?W EX?W EX?W EX?W EX?W EX@X EX?w?X *w Lw Lw Lw LX -X -X -X 5p9X" + "-XEXCX)X%X1X%X1X%X1X%X1X%X LZMZ!XX7X NWFY !V +V +V +V +V +V +Y6W@" + "X ,W5W NW5W NW5W NW5W MW ,W ,W ,W -X7X MX8X X6X X6X X6X X6X X6X $X=_ MW6W MW6W MW6W MW6W " + "LWS >}%~R)~V(~P)W6W NW4W" + " DWJX ?XAW L~^ $X ,X %VCV N\\LS 6aDVAW0XLZ9W 0W !W :PATAP +V KV 2X" + "4X >X &Z =e BW@X DP8[ L^?Z 7X :h EY;\\ \"d >~X ?e LY ;U@W>YAU:W>W Ks KX *X*X,w Lq IX6f+~R" + "'X -X -b 7X -XGWFWGX0XDWCX)X%X.X@^ NX%X.s Bl 8X X IXDVCVDX)[ 4\\ -Z @W *V #W $" + "W JX5W\"X -W5X W4W KW 0W5X MX7W MW ,W ,WIZ =W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LWX >XMX BW6W W#W1WD" + "WDW W=W JWCWCW!W4W#X6X >W >| HW>W 7Y BX 0X%X1X?X?X-X0X'XAXAX.XImIX.Y CV DY DV EY)Y DV " + "DX 2XBS 6X 2WY BSFV'S9bMV ;XFY D~X .S@h>S @~W2i >g W EW>W EW>W EW>W EW>W EW>W EX?w?X *w Lw Lw Lw LX -X -X -X 5" + "p9X-XDWCX)X%X1X%X1X%X1X%X1X%X Ke X=W?X.X/X'X/X'X/X'X/X I\\ >X7X NWEY \"W ,W ,W ,W ,W ,W ,X5W" + "@X -W4W W4W W4W W4W MW ,W ,W ,W -W6X MX7W W4W W4W W4W W4W W4W $W=VMW MW6W MW6W MW6W MW6W " + "LW=X HX5W NW=X MVLuKU/VLuKU/V?[>U/V=Y>U/V=X=U&V 3X=W 7X FW@T ?~&~T*~V)~R*W5V NW4" + "W EXJX ?XBX L~^ $X ,X &VBV Mb 4]CVC]4XJZ:W 0W !W +T KV KV 2X4X >" + "X 'Z X Lu MX *X*X,w Lq IX6f+~R'X -X -c " + "8X -XFVFVFX0XDXDX)X%X.u MX%X.r ?l :X X IXDVCVDX)\\ 4Z ,Y ?W *V #W $W JX5W\"W ,W" + "5X W3W LW 0W5X MX7W MW ,W ,WJY ;W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LWW 6Y 0X 9V LX 5`3R 0T?[?T/W:[ KWId DbKW HW5X NW +X7W JV>W =WLX BW6W W#W1WDWDW W=W JWC" + "WCW!W4W#W4W >W >| IX>X 9Y AX 0X%X1X?X?X-X0X'XAXAX.XImIX/Y BV CY EV FY'Y EV DX 2WAS ?r " + "CV:V =^ =V 2V=Y CSFV'S8`LV e :W6W GbGU IX 4g 8c 5XFX FgFV" + ":YX GX>X GX>X GX>X GX>X GX>X FX?w?X *w Lw Lw Lw LX -X -X -X 5p9X-XDXDX)X" + "%X1X%X1X%X1X%X1X%X Jc NX>W>X.X/X'X/X'X/X'X/X HZ =X7X NWEZ #W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3" + "W!W3W!W3W NW ,W ,W ,W .X5W MX7W W4W W4W W4W W4W W4W $W>VLW MW6W MW6W MW6W MW6W KW>W GX5W " + "MW>W LVLuKU/VLuKU/V>Z>U/V>Y=U/V=X=U&V 2W>X 8Y FW@T ?~P(~V*~T(~Q)V4V NW4W EXJX >W" + "BX L~^ $X ,X &VBV Ld 4WAVD`6XHZ;W 0W !W +T KV LW 2X4X >X 'Y ;i G" + "V>X *Z M\\;Y 9X =p HZ?^ 'd Id$Y 9UAWX GWEVJVEW#a >W>W 7Y 1Y " + "8V KY 9e8T 0T?Z>T0X:[ KWIf GdLW HW4W MW ,W6W JV?X >XKW BW6W W#W2XDWDX!W=W JWCWCW!W4W#W4W" + " >W >| IWX GX>w?X *w Lw Lw Lw LX -X -X -X 5p9X-XCWDX)X%X1X%X1X%", - // Definition of a 40x38 'danger' color logo. - const unsigned char logo40x38[4576] = { + // Second string: + "X1X%X1X%X Ia MX?W=X.X/X'X/X'X/X'X/X GX W GX5W MW>W " + "LVLuKU/VLuKU/V?\\?U/V?YX 8X DWBT ?~Q)~W)~R&~(V4V NW4W EWHW >WBW K~^ " + " $X ,X &VBV Kg \"VEc8WFZ=W /W !W +T 4~W 5V 1X4X >X (Y -] IW>X )Y M[9X 9" + "X >\\F\\ H[C` 'a Ca$Y 9UAV:WAU;WW )V $W 6i JX5X$X -X5X V2W LW 1W3W " + "MW6W MW ,W ,WLY 9W ,W7W7W=W6W!X4X NX5X$X5X MW .[ .W ,W6W KW>W FWEVJVEW#a >W?X 8Z 4\\ 8V K[" + " =iW" + "2W IWX X *X -X -X -X -X -X -X -X ,X*X-XCXEX)X%X1X%X1X%X1X" + "%X1X%X H_ LX@Wi >i >i >i >i >i3WBX ,V2W!V2W!V2W!V2W NW" + " ,W ,W ,W .W4W MW6W!X4X\"X4X\"X4X\"X4X\"X4X M~Y2X@VIW NW6W MW6W MW6W MW6W KW?X GX5X NW?X L" + "VLuKU/VLuKU/V@^@U/V@Y;U/V=X=U&V 2X?W 8X CWBT ?~R*~X)~Q%}(V4W W4W FXHX ?XDX K~^ " + " $X ,X 'WCV Ii &VEe:XEZ>W /W !W +T 4~W 5V 1X4X >X )Y )[ KW=X (Y N[9Y ;Y " + "?Z@Z I]Gb '^ =^$X 9U@V:WAUXIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W3X ?W >W" + "2W JW;X ~R+~Z*~P#{'V4W W4W FXHX ?XDX K~^ $X " + " ,X 'VBV Gi (VFg;WCZ?W /W !W +T 4~W 6W 1X4X >X *Y &Z LW=X (Y NZ7X ;X ?Z>Z ImNX " + "'[ 8\\%Y 9UAW:WAUX XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W JW:W =Y >X 0Y'" + "X0X?X?X-X0X%XCXCX,X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V V&V 1XAW 9" + "X @WDT ?~S+~Z)}!y'W4W W4W FWFW >WDW J~^ *r ?V &VBV Eh *VEXIXX +Y $Z NWXHX DW6W!WW2W KX:X ?Y =X /X'X0Y@X@Y-X0X%YDXDY,X%X2~a " + "GV H~a HV I~b HV DX 3W@S ?r DV8V ;X DW;V DSFV'S >XFX ;V .S@VFW=S (V \"W6W " + ":UGU IX 0XFX -V;TLU MV0U!V;TLU6Y 0X:X KX:X KX:X KX:X KX:X KX:X JWV&V 1XBX :X ?WDT ?~S,~[({ x&W4W W" + "4W FWFX ?XFX JV \"q >V &VBV Af -VEXGX=W@ZBW .W !W +T 4~W 5f 8V 0X4X " + ">X ,Y \"Y W;X 'X NZ7X X -XDVJVDX0XAXGX)X%X.i AX%X.X>Z ,\\ ?X XGW DW6W!WW2W KW9X ?Y =X /X'X/X@X@X,X0X$YEXEY+X%X2~a GV H~a HV I~b HV DX " + "3W@S 6X 3V8V ;X DXWEW :V .TAVEW?T (V \"W6W :UGU IX /WEW .V;" + "TKU NV/U\"V;TKU7Y /X:X KX:X KX:X KX:X KX:X KX:X KXWDS >~T-~\\(y Mw&W4W W4W GXFX ?XFX JV " + " #r >V 'WCV X -Y Y!W;X 'Y Y5X =X" + " @Y8Y HgKX 'a Ca%X 8UAV8VAU=W8W NX4X%X *X+Y,X -X 0X(X+X/X'X -X -XI[ ?X -XDWLWDX0X@WG" + "X)X&Y.X 0X&Y.X=Y *[ @X XFX EW6W!WW2W KW8W @Y ] Jt It It It It It I~iBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W" + "2W\"W2W\"W2W\"W2W\"W2W M~Y2WCVEW NW6W MW6W MW6W MW6W IWCX EW3W LWCX IV=V=V.V$V.VFYKZFV.VFY" + "7V.V$V&V 0XCW ;Y =WFT >~T-~\\'w Ku%W4W W4W GXEW >WFW IV #q =V 6~X " + "JSN^ /VEWCW?W=ZDW .W !W :~W 5f 9V /X4X >X .Y MX\"W:X &X Y5X >Y @X6X FcJX &d Id" + "%X 8UAV8VAU>X8X X4X$X +X+X+X -X /X)X+X/X'X -X -XH[ @X -XCVLVCX0X@XHX(X'X-X /X'X-XXFX EW6W!WV-V%V-VGYIZHV-VGY7V-V%V%V " + "/WDX ;X ~T-~\\'v Is$W4W W4W GWDX ?XGW HV %r =V 6~X JSJ[ 0VEV" + "AV?WX ?X6X D`IX $d Ne#X 8UAV8" + "VBU=x X4X$X +X+X+X -X /X)X+X/X'X -X -XG[ AX -XCVLVCX0X?WHX(X'X-X /X'X-X;Y *Y @X WDW EW6W!WV>V,V&V,VIYGZIV,VIY6V,V&V&W /XEW N~X'VGT =~T-~\\" + "&u Ir#W4W NV4W HXDX ?XHX HV KX ,V 6~X JSHZ 2VDVAV?W;ZGW -W !W \"V " + "Lf :W .X6X =X 0Z LY#~ /X NX5X >X @X5Y AYFX !d >~X >d X 8UAV8VBU>z!X3X%X +X+X+X -X /X" + ")X+X/X'X -X -XF[ BX -XCWNWCX0X?XIX(X'X-X /X'X-X:X )Y AX XDX FW" + "6W!WV?W,V'W,VJYEZKW,VJY6W,V'W&W /XFX N~X'WHT =~T-~\\%s Gp\"W4W NV4V GXCW >WH" + "X HW LX ,V 6~X JSGY 3VDWAW@W:ZIW ,W !W \"V Lf :W .X6X =X 1Z JX#" + "~ /X NX5X ?Y @X4X .X Md A~X Ad LX 8UAV8VBU>z!X3X%X +X+X+X -X /X)X+X/X'X -X -XE[ CX -XB" + "VNVBX0X>WIX(X'X-X /X'X-X9X *Y AX Q.X $T>Z?T0W8W HW5W\"WWCX FW6W!WXFX >V ,SBVBWCS &V \"W6W :UGU *m 8XFX .VWIX(X'X/X'X/X'X/X'X/X'X KZMZ XHW6X-X" + "/X'X/X'X/X'X/X GX XIW GW LX " + " ;~X JSFX 3VDV?V@W9ZJW +V \"W !V V -X6X =X 2Z IX#~ /X NX5X ?X ?X4X .X Jd D~" + "X Dd IX 8UAV8VCV>z!X3X%Y ,X,Y+X -X /Y*X+X/X'X -X -XD[ DX -XBVNVBX0X>XJX(Y)X,X /Y)X,X9Y *X " + "AX XBW FW6W!WXJX(Y)X.Y)X.Y)X.Y)X.Y)X KZKZ!YJW6X,X/X'X/X'X/X'X/X GX |\"X3X$X ," + "X,X*X -X .X*X+X/X'X -X -XC[ EX -XA\\AX0X=WJX'X)X,X .X)X,X8X *X AX XBX GW6W!WW 9X =\\KW >SEWWJX FW LX <~X JSEX 6WCV?V@W7ZMW *W #W !V !W -X6" + "X =X 4Z GX#~ /X NX5X @X >X4X /X De J~X Je DX 8U@V:WDV>|\"X3X$X ,X-Y*X -X .X*X+X/X'X -X" + " -XB[ FX -XA\\AX0X=XKX'X*Y,X .X*Y,X8Y +X AX W WJW DW" + " MX .VCV :SDW 6VBV?V@W6b )W #W !V !V +X8X X4X /X Ad L~X Ld AX 8VAV:WDU=|\"X3X$Y -X-Y*X -X .Y+X+X/X'X -X -XA[ GX -XA\\AX0XWKVDVKW\"XLX 9WJW =Z #X" + " :V MX AUEVKVDU/X:Y IW5W#WX@W GW6W!W=Y=W2WDWDW W=W JWCWCW\"X4W#W4" + "W >W X4X 0X =d ~X" + " d LUAWX2X#X3X#X -X.Y)X -X -X+X+X/X'X -X -X@[ HX -X@Z@X0XW " + ",W7W7W=W6W W4W MX5W\"W5X MW BX FW ,W7X FWHW >WLVBVLW#YKX :WJW =Y !W :V MW @VHXJWHV-W:" + "Y IW5W#WY>W1WDWDW W=W JWCWCW\"X4W#W4W >W W MW7X MW7X MW7X MW7X EWJW AX5W GWJW AXCVCW%X0W%X0W%X0W%X0W\"V +WJX " + " ?X 2WLT 9bKQKb)gLQMh Mi =g MW4W MV6W IX@X ?XLX CW MX 0VBV :SDW " + "7VAV?V@X5_ (W #W !V \"W +X8X XL" + "V;VLX1Y?Y >X 9Z 2W %W )W EW7X JX5W\"X -W5X X )W 0X7Y MW6W MW ,W ,WFY ?W ,W7W7W=W6W W4" + "W MX5W\"W5X MW AW FW ,W7X FXJX =WMVBVMW#YJY ;WKX >Y W :V MW ?dId,W;Z IW5W#W=W DW4W!W" + " )W6W DVKW >X>W HW6W W>Y>W1WDWDW W=W JWCWDX\"X4W#W4W >W ;V7W LX2X LY 4X *X1X%]JXJ]'X0X Hj L" + "Y-Y%Y IV JY LYKVKY MY5Y MYJVJY $X 2XBS 6X 2q 9X :V #\\ 7TDgFT /XFX EV )TFV>VJT #" + "V \"W6W :UGU +XFX *V=TCU%V1V!V=TCU=X ,X1W$X1W$X1W$X1W$X1W$X2X%X7X LY .X -X -X -" + "X -X -X -X -X ,X.Y*X;XMX&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y ZAZ$_3Y*X1X%X1X%X1X%X1X FX W3W$W7X MW7X M" + "W7X MW7X MW7X MW7X MW7Z NX -X -X -X -X +W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W 5Z IWMV=W" + " MW7X MW7X MW7X MW7X EWKX AX5W GWKX @XDVDX$X2X$X2X$X2X$X2X\"V +XKW ?X 1WMT 7`JQKa" + "'fLQLf Kg W >WLW BX NY 1VBV :SDW 8V@V?V?W4] &V $W " + " V \"V *Y:Y YGW>X0X$X4Y\"Y /X/Y(X -X" + " ,Y-X+X/X'X -X -X>[ JX -X@Z@X0X;XMX%Y/Y*X ,Y/Y*X6Y -X AX ;Y3Y IXLX =WLV;VLW0X=Y ?X :Z 1W $" + "V )W EW8Y JY7X\"X -X7Y X )W 0X7Y MW6W MW ,W ,WEY @W ,W7W7W=W6W X6X MY7X\"X7Y MW AW FW" + " ,X8X EWJW Y NW :V MW >bGc,W;[ JW6X#W=W DX6X!W )W6W DVLX >W=X IW7" + "X W>Y>W1XEWEX W=W IWDWDW!Y6X#X6X >W ;W8W MX0X MY 4X *Y3Y$^LXL^&X0X Ff IY/Y#Y JV KY JYLVL" + "Y KY7Y KYKVKY #X 2XBS 6X 3t ;X :V ![ 8TCfFT .XFX FV )UGV>WKT MW7X :UGU " + " ,XFX *V=TBU&V2W!V=TBU=X -X0X&X0X&X0X&X0X&X0X&X0W%X7X KY /X -X -X -X -X -X -X -X ,X/Y)" + "X;XMX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y Z?Z$^4Y)Y3Y%Y3Y%Y3Y%Y3Y FX XEVFY\"X5Y\"X5Y\"X5Y\"X5Y!V *WLX @X /WNT 7`JQJ_&eKQKe Je :d KW4W MW8" + "W HW>X ?XNX AX Y 1VCV 9SDW 9V?V?V?X4\\ &W %W V \"V )X:X ;X 9Z" + " CX 4X (Y KW7X AX W BW6W W )W6W DWMX ?X=X IX8X W?[?W0WEWEW NW=W " + "IWDWDW!Y6W!W6W =W ;W8W MX0X NY 3X )Y5Y\"z%X0X C` FY/Y\"X JV KX HYMVMY IX7X IYLVLY \"X 1XCS" + " 6X 4v X ?XNX AY " + " Y4P VBV 9SDW 9V?V?V?Y4Z %W %W V #W )X:X ;X :Z CY 4X (Y KX9Y AX ;X6X 1" + "Y 1e /e @U@XB[JXW BX8X W )W6W CVNX >W;W IX8X X@[@X0XFWEW NW=W IWDWEX!Z8X!X8X =W :W:W LX" + "0X Y 2X (Y7Y Nv#X0X ?X AY1Y V IV JV FYNVNY GV5V GYMVMY !X 1XCS 6X 5x =X :V MZ 8T?ZBT" + " *VDV FV 'T&T KX8X :UGU ,VDV )VWNX @Y !Z6Q VBV K" + "P>SEW 9V>WAW>X3Z &W %W V #V 'XU?" + "ZH^MZ\\ JX8X\"W?W AX" + "9Y X *W6W CVNX ?X;X JX9Y NW@[@W/XFWFX NW=W IXEWEX!Z8X!X8W ;W ;W;X MX.X\"Y 1X 'Y9Y Lt\"X0X ?X" + " @Y3Y MT HV IT Dj ET3T EYNVNY X 0XDS 6X 6ZM`LY >X :V LY 7T)T (UCU ET(T " + " JX9Y :UGU ,UCU )V;m.V3V NV;mCY7P HX.X(X.X(X.X(X.X(X.X(X.X(X6X IY.R&X -X -X -X -" + "X -X -X -X ,X2Z'X9a$Z3Y&Z3Y&Z3Y&Z3Y&Z3Y!Z9Z&Z3Y&Y5Y#Y5Y#Y5Y#Y5Y EX ` >Y !Y8S MX +VBV KQ?SFX 9V=VAV=Y6] &V &W" + " NV BX 1X 1V 'Y>Y :X X:W JY;Z NXB]BX.XGWGX MW=W HXFWFX [:X NX:X ;W :WX HXX 9X =Z 1P2Z 3X GQ5Z GX=Y @X 9Y:Y KP8Z GX -X 4^ 1^ +X 5U?gM_9W,W%X7Z L[4U&X6]%X -X )" + "[2X+X/X'X -X -X9[ X -X&X0X8`\"Z7Z'X )Z7Z'X3X%T2Y ?X 9Z9Z E` :_9_3Y7Y BX >Z -W #W +W D" + "X=\\ J\\=Y LY7P HY=\\ LY5R JW -Y?] MW6W MW ,W ,W@Y EW ,W7W7W=W6W MYX LX.X#Y 0X %Y=Z Gl MX0X ?X ?Z7Z JP FV GP @f AP/P Ah MX " + "/YFSDP BX 8ZFVEY @X :V JX 7V.U %SAS CU.U HZ" + "\\=Y B^ 7r Gr Gr Gr Gr KV (_ BX )Y S 8RBSCR <] 2\\ GW4W KZBZ HX;W >_ <[ " + " $[=U MX ,VBV JUCSHY :V;WCW<[Z 0R5Z 2X GT9[ G" + "Y?Z AY 9[>[ KR;Z FX -X 1[ 1[ (X 5V>dL^9X,X&X9[ J[7W&X9_$X -X (\\6Z+X/X'X -X -X8[!X -X&X0X" + "8`![;[&X ([;[&X3Y&W7[ ?X 8Z;Z D` :^7^3X5Y CX ?Z ,W #W +W DY?] J]?Y KZ:R GY?] LZ8T JW" + " -ZA^ MW6W MW ,W ,W?Y FW ,W7W7W=W6W LY>Y J]?Y KY?] MW /T9X DX ,Y@] CWNW 9]>]'Y@Y =^ AY IW" + " :V MW HYCXNW L\\>Y VAX >Y>Y LY ,W6W B] >X9X K[>[ MXDVMVDX,YIWIY LW=W GYHWHY N]>Y LY" + ">Y :X :X@X LX,X%Y /X $ZAZ Ch KX0X ?X >[;[ ?V 6d >f LX /[HSFR BX 9Z3Y AX :V IX 7" + "V1V #R@R BU0U G[>[ :UGU ,R@R 'V(U)V6W LV(UU IX,X*X,X*X,X*X,X*X,X" + "*X,X*W4X G[7W&X -X -X -X -X -X -X -X ,X9_%X8`![;[![;[![;[![;[![;[\"Z3Z(];[\"Z;Z NZ;Z NZ;Z NZ" + ";Z CX Y JW6W LY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y HY@] KY@] KY@] KY@] B^ >]?Y A^ 6o Do Do Do " + "Do IV (_ CX (Y S (S ,[ 0[ GW4W J\\H\\ GW:W >^ :\\ %[@W MX ,VBV JXFS" + "IZ :V:WEW:\\@e (V 'V MV BX 1X 2V $ZDZ 8X ?Z /U;] 2X GV=\\ EZC[ @X 7[@[ JT?[ EX -X /Y " + " 1Y &X 5V=bK\\7X,X&X<^ I]=Z&X=b#X -X ']:\\+X/X'X -X -X7[\"X -X&X0X7_ \\?\\%X '\\?\\%X2X&Z<\\ >X 7[" + "?[ B^ 9^7^4Y5Y CX ?Y +W \"V +W DZB_ J_CZ I[>T G[C_ K[=W JW ,\\GXNW MW6W MW ,W ,W>Y GW " + ",W7W7W=W6W KZBZ I_CZ J[C_ MW /W>Z DZ .ZB^ C` 8\\>\\&X>Y =\\ AY HW :V MW GZFYNY N]AZ N" + "WCX _ FX0X ?X =\\?\\ >V 5b W;[>T F[=W J[=W J[=W J[=W LW ,W ,W ,W *ZBZ IW6W KZBZ GZBZ " + "GZBZ GZBZ GZBZ 1Z F[BZ GZB^ KZB^ KZB^ KZB^ A\\ =_CZ ?\\ 3l Al Al Al Al HV (^ BX (X " + " NS (S ,Z .Y FW4W In GX:X ?^ 9_ (]FZ MX ,VBV J[ISL\\ :V9XGX9^Fi )W )W " + " MV BX 1X 3W #[H[ Et Mx MZC_ 1X GZD^ C[G\\ @Y 7^F] IXF] DX -X ,V 1V #X 4V<^IY5X*X'y G" + "_D^&{!y NX &`B`+X/X'X -X -X6[#w LX&X0X7_ N^E^$X &^E^$X2Y'^C^ =X 7^E^ B^ 8]7]4Y3Y DX @~U&W " + "\"W ,W C\\HYNW JWNXG\\ H]EX F\\GXNW J]D[ JW +kMW MW6W MW ,W ,W=Y HW ,W7W7W=W6W K]H] IWNX" + "G\\ I\\GXNW MW /[E\\ Be 9[GXNW B^ 7\\>\\'XP @W8W 3~W :_GaKP" + " @UGU ,P>P 'V&U+V6V KV&U;]GZ JX*X,X*X,X*X,X*X,X*X,Y,Y,X4y7_D^&y Ny Ny Ny NX -X -X -" + "X ,{\"X7_ N^E^ L^E^ L^E^ L^E^ L^E^ MV/V(dE^ N^E^ L^E^ L^E^ L^E^ BX \\ Av 6W :V MW FkL]$u LXGX 9p Hp EW6W A[ ?X6X LpN\\#hKh)s JW<] Lu LWNm Hp 6` Bl K~" + "W'x MX 1iEi HX CX0X ?X ;u X :V HW 3X=X )X\\ " + " /c 8c 8c 8c 8c CV '\\ ?T %W U *T *W ,V DW4W Gj EW8W >\\ 5~P In LX " + " -VBV Is 9V7g6qJZ *V )V LV BX 1X 3V !l Dt Mx Mt /X Gr ?m ?X 4r Hm BX -X &P 1P LX" + " 3V 3X*X'w Cv%x My NX #x(X/X'X -X -X4[%w LX&X0X5] Ls\"X $s\"X1Y(w ;X 5s ?\\ 7\\5\\5Y1Y EX @~U&W" + " !V ,W BjLW JWMj Dn DjMW Hr JW )hLW MW6W MW ,W ,W;Y JW ,W7W7W=W6W In GWMj EjMW MW /p" + " ?d 8iLW B^ 6Z<[)Y:Y >Z @v 6W :V MW EiK]$t JYLZ 7n Fo EW6W A[ ?X5W LWNfM\\\"gKg'q IW<" + "] Ks KWMk Fn 5` Aj J~W'x MX 1iEi HX CX0X ?X :s ;V 2\\ 6^ HX +n Lz MR,R =X :V HW " + "1ZEZ %ZDZ 0~W :WNfM\\ @UGU !V%U,V6i/V%U9n JX*X,X*X,X*X,X*X,X*X,X*X-" + "X3y5v%y Ny Ny Ny NX -X -X -X ,x NX5] Ls Hs Hs Hs Hs IR+R(WMs Js Hs Hs Hs @X R $V NU *U *U *U DW4W Fh DW8X ?\\ " + "4~ Hl KX -VBV Hp 8V5e4nGZ +W +W LV BX 1X 3V j Ct Mx Mr -X Gq =j " + ">Y 3p Gl AX -X 2X 3W 5X(X(u ?s$v Ky NX \"v'X/X'X -X -X3[&w LX&X0X5] Kq!X #p X0X(v :X " + "4p =\\ 7\\5\\6Y/Y FX @~U&W !V ,W AhKW JWLh Bm ChLW Gq JW (eJW MW6W MW ,W ,W:Y KW ,W7W7W" + "=W6W Hl FWLh ChLW MW /o >d 7gKW A\\ 5ZZ @v 6W :V MW DgI\\$s He 5l Dn EW6W @Y " + ">W4X MWMeM\\!eIe%o HW<] Jq JWLi Dk 2_ @h J~Y(x MX 1iEi HX CX0X ?X 9q :V 1Z 4\\ GX *m" + " Lz LP*P X X ?v 6W :V MW CeG[$r Fc " + "2h Am EW6W @Y ?X3W MWMdL\\ cGc#m GW;\\ Hm HWKg Ah /] ?f I~Y(x MX 1iEi HX CX0X ?X 7m 8V 0" + "X 2Z FX (j Kz AX :V HW -g Lh ,~W :WMdL\\ @UGU \"V$U-V5i0V$" + "U7i HX(X.X(X.X(X.X(X.X(X.X(X/X2y1o\"y Ny Ny Ny NX -X -X -X ,t JX4\\ Im Bm Bm Bm Bm %VHm Dm " + "Bm Bm Bm =X eJW GeJW GeJW GeJW ?X ;WJe 9X MW &Z =U W ,W *" + "R &Q BW4W B` AW6W >[ /y Dd GX -VCV Af 5V2a.gBZ ,W -W KV CX 0X 4V " + " Kd @t Mx Km *X Ek 6d ;X .h Bh >X .X 1X 1W 7X(X(q 7j Np Ey NX Mm\"X/X'X -X -X1[(w LX" + "&X0X4\\ Gi LX Ni LX/X$n 7X 0i 9Z 5[5[6Y-Y GX @~U&W V -W >cIW JWIb k EW6W @Y ?W2W MWK`I[ NaEa i EW;\\ Fi FWIc >e ,\\ =b G~Y(x MX 1iEi HX CX0" + "X ?X 5i 6V /V 0X EX &f Iz AX :V /P;W *c Gb )~W :WK`I[ @UGU " + " #V#U.V4i1V#U6f FX(X.X(X.X(X.X(X.X(X.X(X/X2y/j Ny Ny Ny Ny NX -X -X -X ,p FX4\\ Gi >i " + ">i >i >i $VEi @i >i >i >i ;X i0g ;i >i >i >i HW " + ",W ,W ,W #d BW6W Ef ;f ;f ;f ;f JUJe ;cIW FcIW FcIW FcIW ?X ;WIb 7X MW %Y " + " =T X -X )P %P AW4W ?Z >W6X ?Z ,w B` EX .VBV <] 1V0]*b?[ -W -W" + " KV CW /X 4V I` >t Mx Hg 'X Bf 2` :X +d =b ;X .W 0X 1X 9X&X)m 0d Kj ?y NX Jg " + "NX/X'X -X -X0[)w LX&X0X3[ Dc IX Kf LX/Y!g 4X .e 7Z 5Z3Z7Y+Y HX @~U&W V -W =`GW JWG" + "^ 7b 9^GW Ad CW \"YDW MW6W MW ,W ,W7Y NW ,W7W7W=W6W B` @WG^ 9^GW MW (c 2] 3_GW @Z 3X:X*Y4Y " + "@X ?v 6W :V MW ?_AW$WKb @^ +` 9g CW6W ?W ?X2X NWJ^GY K]B^ Ke CW:[ Dd CWG_ 9` 'Y ;^ " + "F~[)x MX 1iEi HX CX0X ?X 2c 3V .T .V DX $b Gz AX :V /R>X &[ ?Z %~W " + " :WJ^GY ?UGU #V +V +V 1b EX&X0X&X0X&X0X&X0X&X0Y'X1X1y,d Ky Ny Ny Ny NX -X -X " + "-X ,j @X3[ Dc 8c 8c 8c 8c !VBc ;e :e :e :e 9X Y BS .V,W#Z ;V -V " + " 7W ;W EX ;\\ 6] +Z 5\\ 5Z WGXBU FX=X E` \"W >] @WDY 3Z " + "2X C[ >T :[ KV /TAY EWGXBU =UGU" + " BT 6V +V +V ,Y ?\\ +[ 0[ 0[ 0[ 0[ KT=[ 2[ 0[ 0[" + " 0[ 7Z ;Y .Y .Y .Y .Y .Y -Y2\\\"Z /\\ 1\\ 1\\ 1\\ CZ 3Z /Z /Z /Z /Z FVCZ 1Y .Y ." + "Y .Y ,W :WDX 2W LW 7R #S" + " >W /W 8W :V \"W 5X )X " + " &Z CW NV .W :W %W @W :W " + " -X -W :V MW LW FW ?W >W NW 0W =W " + " 3S GV /XGZ DW HUGU AT %" + "T 'R JT " + " #T (X :W NX LW " + " 7S =V /V 7W :V \"W 4X'Q " + "&Y %Z DW NV .W :W %W @W :W " + " -W ,W :V MW LW FW ?W >W NW 0W =W " + " 3S GV /j CW HUGU @T " + " %T 'P HT " + " \"Q 'W 9W NW KW " + " 7S =W 1W 7V :W \"V 2X)R " + " &X #Z EW NW /W :W %W " + " @W :W -W ,X ;V NX LW FW ?W >W NW 0W =W " + " 3S GV /j CW HUGU @U " + " &U U " + " \"P 'W 9W NW KV " + " 6S W NW 0W =W " + " 3S GV /h AW HUGU ?T " + " %T NT " + " )X 9W X KV " + " 6S W NW 0W =W" + " 3S GV .f @W HUGU ?" + "U &U " + " U *W 8W W JV " + " 6S ;V 3V 6V :W \"V " + " .[5[ *Y Z Ha (W :a W NW 0W" + " =W 3S GV +a >W HUGU " + " >T %T " + " NT +X 8W !X (VIV " + " 6S :V 5V 5U 9W \"" + "U +\\;] )X MZ Ia (W :a " + " =Y %W ?W :W /W )[ ?V #[ KW FW ?W >W N" + "W 0W =W 3S GV 'Z ;W " + " HUGU >U &U " + " U ,W 7W !W 'VIV " + " 6S :V 6W 6V " + " 4V *_C` )Y LZ Ja :a " + " (P7Y $W ?W :W 0X (b GV +b JW FW ?W >W " + " NW 0W =W 3S GV " + "7W HUGU >U &U " + " U -X 7W \"X 'VJW " + " 6S 9V 7V 5U " + " 3U 'x (Z KZ Ka :a " + " (R:Z $W ?W :W 0X (b GV +b JW FW ?W >W" + " NW 0W =W 3S GV " + " 7W #U &U " + " U -X 7W \"X &UJW " + " 6S 9W 9W " + " Bu ([ IZ La :a " + " (T>[ $X ?W :W 1X &a GV +a IW FW ?W >W N" + "W 0W =W 3S GV 7W " + " $V 'V " + " !V .X 6W #X %VLW " + " 5S " + " 2p -a 8XE] %Y" + " >W :W 3Z $_ GV +_ GW FW ?W >W NW 0W =W " + " 3S GV 7W /QGW " + " 2QGW ,QG" + "W 0Z 6W %Z %a " + " 5S 0l " + " +a 8p +_ " + " >W :W ;a !] GV +] EW FW ?W >W NW 0W =W " + " 3S GV 7W /` " + " 1` +` " + " 7a 5W -a #` " + " >e '`" + " 7o *^ =W :W " + " ;` KY GV +Y AW FW ?W >W NW 0W =W " + " 3S GV 7W /` 1` " + " +` " + " 7` 4W -` \"_ " + " 8\\ #_ " + " \"} 3n )^ =W :W ;` 9V " + " BW FW ?W >W NW 0W =W 'V " + " 7W /_ 0_ " + " *_ 6` 4W -` " + " !] " + " -] " + " } 3l '] W NW 0W =W 'V " + " 7W /^ /^ " + " )^ 5_ 3W -_ N[ " + " " + " ,[ M} 2j " + " &\\ ;W :W ;^ 7V BW FW ?W >W NW 0W =W" + " 7W -Y " + " *Y $Y " + " 2^ 2W -^ LX " + " " + " *X J} /d #Z 9W :" + "W ;\\ 5V BW FW ?W >W NW 0W =W " + " 7W " + " " + " /\\ 0W HT " + " " + " I} *[ NW 6W :W ;Z 3V " + " BW FW ?W >W NW 0W =W " + " 7W " + " /Z .W " + " " + " =} " + " " + " " + " " + " D" }; + + // 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, @@ -3882,51 +4753,108 @@ namespace cimg_library { 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}; + 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 }; - //! Set/get output stream for CImg library messages. + //! 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) { - static std::FILE *res = stderr; + cimg::mutex(1); + static std::FILE *res = cimg::_stderr(); if (file) res = file; + cimg::mutex(1,0); return res; } - //! Display a warning message. + // 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 describing the format of the message, as in std::printf(). + \param format C-string containing the format of the message, as with std::printf(). + \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 message[16384] = { 0 }; + char *const message = new char[16384]; std::va_list ap; va_start(ap,format); - cimg_vsnprintf(message,sizeof(message),format,ap); + 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",cimg::t_red,cimg::t_normal,message); + 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 std::system() - and is here because using the std:: version on - Windows may open undesired consoles. + 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) { -#if cimg_OS==2 + cimg::unused(module_name); +#ifdef cimg_no_system_calls + return -1; +#else +#if cimg_OS==1 + const unsigned int l = (unsigned int)std::strlen(command); + if (l) { + char *const ncommand = new char[l + 16]; + std::strncpy(ncommand,command,l); + std::strcpy(ncommand + l," 2> /dev/null"); // 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)); @@ -3934,190 +4862,184 @@ namespace cimg_library { GetStartupInfo(&si); si.cb = sizeof(si); si.wShowWindow = SW_HIDE; - si.dwFlags |= 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); + WaitForSingleObject(pi.hProcess,INFINITE); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; - } else + } else return std::system(command); +#else + return std::system(command); +#endif #endif - return std::system(command); - return module_name?0:1; } - //! Get a reference to a temporary variable of type T. + //! Return a reference to a temporary variable of type T. template inline T& temporary(const T&) { static T temp; return temp; } - //! Exchange values of variables \p a and \p b. + //! Exchange values of variables \c a and \c b. template inline void swap(T& a, T& b) { T t = a; a = b; b = t; } - //! Exchange values of variables (\p a1,\p a2) and (\p b1,\p b2). + //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2). template inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { cimg::swap(a1,b1); cimg::swap(a2,b2); } - //! Exchange values of variables (\p a1,\p a2,\p a3) and (\p b1,\p b2,\p b3). + //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3). template 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 (\p a1,\p a2,...,\p a4) and (\p b1,\p b2,...,\p b4). + //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4). template 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 (\p a1,\p a2,...,\p a5) and (\p b1,\p b2,...,\p b5). + //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5). template 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 (\p a1,\p a2,...,\p a6) and (\p b1,\p b2,...,\p b6). + //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6). template 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 (\p a1,\p a2,...,\p a7) and (\p b1,\p b2,...,\p b7). + //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7). template 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 (\p a1,\p a2,...,\p a8) and (\p b1,\p b2,...,\p b8). + //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8). template 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); } - //! Get the current endianness of the CPU. + //! Return the endianness of the current architecture. /** - \return \c false for "Little Endian", \c true for "Big Endian". + \return \c false for Little Endian or \c true for Big Endian. **/ inline bool endianness() { const int x = 1; return ((unsigned char*)&x)[0]?false:true; } - //! Invert endianness of a memory buffer. + //! 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 - inline void invert_endianness(T* const buffer, const unsigned int size) { + 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; - 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)); - } - } - } + 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; + 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)); + } + } + } } - //! Invert endianness of a single variable. + //! Reverse endianness of a single variable. + /** + \param[in,out] a Variable to reverse. + \return Reference to reversed variable. + **/ template inline T& invert_endianness(T& a) { invert_endianness(&a,1); return a; } - // Conversion function to gain more precision in storage of unsigned ints as floats. + // 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; - std::memcpy(&u,&f,sizeof(float)); // use memcpy instead of assignment to avoid wrong optimizations with g++. + // 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 until 19bits (i.e 524287). + 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. - std::memcpy(&f,&v,sizeof(float)); // use memcpy instead of simple assignment to avoir wrong optimizations with g++. + // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. + std::memcpy(&f,&v,sizeof(float)); return f; } - //! Get the value of a system timer with a millisecond precision. - inline unsigned long time() { + //! 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 (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000); + return (cimg_ulong)(st_time.tv_usec/1000 + st_time.tv_sec*1000); #elif cimg_OS==2 - static SYSTEMTIME st_time; - GetSystemTime(&st_time); - return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); + 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 unsigned long tictoc(const bool is_tic) { - static unsigned long t0 = 0; - const unsigned long t = cimg::time(); - if (is_tic) return (t0 = t); - const unsigned long dt = t>=t0?(t - t0):cimg::type::max(); - const unsigned int - edays = (unsigned int)(dt/86400000.0), - ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0), - emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0), - esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0), - ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0); - if (!edays && !ehours && !emin && !esec) - std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u ms%s\n",cimg::t_red,ems,cimg::t_normal); - else { - if (!edays && !ehours && !emin) - std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u sec %u ms%s\n",cimg::t_red,esec,ems,cimg::t_normal); - else { - if (!edays && !ehours) - std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u min %u sec %u ms%s\n",cimg::t_red,emin,esec,ems,cimg::t_normal); - else{ - if (!edays) - std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u hours %u min %u sec %u ms%s\n",cimg::t_red,ehours,emin,esec,ems,cimg::t_normal); - else{ - std::fprintf(cimg::output(),"%s[CImg] Elapsed time : %u days %u hours %u min %u sec %u ms%s\n",cimg::t_red,edays,ehours,emin,esec,ems,cimg::t_normal); - } - } - } - } - return t; - } + inline cimg_ulong tictoc(const bool is_tic); - inline unsigned long 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); } - inline unsigned long toc() { + //! 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 certain numbers of milliseconds. + //! Sleep for a given numbers of milliseconds. /** - This function frees the CPU ressources during the sleeping time. - It may be used to temporize your program properly, without wasting CPU time. + \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 @@ -4127,47 +5049,143 @@ namespace cimg_library { nanosleep(&tv,0); #elif cimg_OS==2 Sleep(milliseconds); +#else + cimg::unused(milliseconds); #endif } - inline unsigned int _sleep(const unsigned int milliseconds, unsigned long& timer) { + inline unsigned int _wait(const unsigned int milliseconds, cimg_ulong& timer) { if (!timer) timer = cimg::time(); - const unsigned long current_time = cimg::time(); - if (current_time>=timer+milliseconds) { timer = current_time; return 0; } - const unsigned long time_diff = timer + milliseconds - current_time; + const cimg_ulong current_time = cimg::time(); + if (current_time>=timer + milliseconds) { timer = current_time; return 0; } + const unsigned int time_diff = (unsigned int)(timer + milliseconds - current_time); timer = current_time + time_diff; cimg::sleep(time_diff); - return (unsigned int)time_diff; + return time_diff; } - //! Wait for a certain number of milliseconds since the last call. + //! Wait for a given number of milliseconds since the last call to wait(). /** - This function is equivalent to sleep() but the waiting time is computed with regard to the last call - of wait(). It may be used to temporize your program properly. + \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 unsigned int wait(const unsigned int milliseconds) { - static unsigned long timer = 0; + inline cimg_long wait(const unsigned int milliseconds) { + cimg::mutex(3); + static cimg_ulong timer = 0; if (!timer) timer = cimg::time(); - return _sleep(milliseconds,timer); + cimg::mutex(3,0); + return _wait(milliseconds,timer); + } + + // Random number generators. + // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set. + // Use it for instance when you have to deal with concurrent threads trying to call std::srand() + // at the same time! +#ifdef cimg_use_rng + +#include + + // Use a custom RNG. + inline unsigned int _rand(const unsigned int seed=0, const bool set_seed=false) { + static cimg_ulong next = 0xB16B00B5; + cimg::mutex(4); + if (set_seed) next = (cimg_ulong)seed; + next = next*1103515245 + 12345U; + cimg::mutex(4,0); + return (unsigned int)(next&0xFFFFFFU); } - // Use a specific srand initialization to avoid multi-threads to have to the - // same series of random numbers (executed only once for a single program). inline void srand() { - static bool first_time = true; - if (first_time) { - std::srand(cimg::time()); - unsigned char *const rand_ptr = new unsigned char[sizeof(unsigned int)+std::rand()%2048]; - std::srand((unsigned int)std::rand() + *(unsigned int*)(void*)rand_ptr); - delete[] rand_ptr; - first_time = false; - } + const unsigned int t = (unsigned int)cimg::time(); +#if cimg_OS==1 + cimg::_rand(t + (unsigned int)getpid(),true); +#elif cimg_OS==2 + cimg::_rand(t + (unsigned int)_getpid(),true); +#else + cimg::_rand(t,true); +#endif } - //! Get a left bitwise-rotated number. + inline void srand(const unsigned int seed) { + _rand(seed,true); + } + + inline double rand(const double val_min, const double val_max) { + const double val = cimg::_rand()/16777215.; + return val_min + (val_max - val_min)*val; + } + +#else + + // Use the system RNG. + inline void srand() { + const unsigned int t = (unsigned int)cimg::time(); +#if cimg_OS==1 || defined(__BORLANDC__) + std::srand(t + (unsigned int)getpid()); +#elif cimg_OS==2 + std::srand(t + (unsigned int)_getpid()); +#else + std::srand(t); +#endif + } + + inline void srand(const unsigned int seed) { + std::srand(seed); + } + + //! Return a random variable uniformely distributed between [val_min,val_max]. + /** + **/ + inline double rand(const double val_min, const double val_max) { + const double val = (double)std::rand()/RAND_MAX; + return val_min + (val_max - val_min)*val; + } +#endif + + //! Return a random variable uniformely distributed between [0,val_max]. + /** + **/ + inline double rand(const double val_max=1) { + return cimg::rand(0,val_max); + } + + //! Return a random variable following a gaussian distribution and a standard deviation of 1. + /** + **/ + inline double grand() { + double x1, w; + do { + const double x2 = cimg::rand(-1,1); + x1 = cimg::rand(-1,1); + w = x1*x1 + x2*x2; + } while (w<=0 || w>=1.0); + return x1*std::sqrt((-2*std::log(w))/w); + } + + //! Return a random variable following a Poisson distribution of parameter z. + /** + **/ + inline unsigned int prand(const double z) { + if (z<=1.0e-10) return 0; + if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z); + unsigned int k = 0; + const double y = std::exp(-z); + for (double s = 1.0; s>=y; ++k) s*=cimg::rand(); + return k - 1; + } + + //! Cut (i.e. clamp) value in specified interval. + template + inline T cut(const T& val, const t& val_min, const t& val_max) { + return valval_max?(T)val_max:val; + } + + //! Bitwise-rotate value on the left. template - inline T rol(const T a, const unsigned int n=1) { - return n?(T)((a<>((sizeof(T)<<3)-n))):a; + inline T rol(const T& a, const unsigned int n=1) { + return n?(T)((a<>((sizeof(T)<<3) - n))):a; } inline float rol(const float a, const unsigned int n=1) { @@ -4175,13 +5193,23 @@ namespace cimg_library { } inline double rol(const double a, const unsigned int n=1) { - return (double)rol((long)a,n); + return (double)rol((cimg_long)a,n); } - //! Get a right bitwise-rotated number. + 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 - inline T ror(const T a, const unsigned int n=1) { - return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a; + 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) { @@ -4189,32 +5217,41 @@ namespace cimg_library { } inline double ror(const double a, const unsigned int n=1) { - return (double)ror((long)a,n); + return (double)ror((cimg_long)a,n); } - //! Get the absolute value of a number. - /** - \note This function is different from std::abs() or std::fabs() - because it is able to consider a variable of any type, without cast needed. - **/ + 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 - inline T abs(const T a) { + inline T abs(const T& a) { return a>=0?a:-a; } inline bool abs(const bool a) { return a; } - inline unsigned char abs(const unsigned char a) { - return a; + inline int abs(const unsigned char a) { + return (int)a; } - inline unsigned short abs(const unsigned short a) { - return a; + inline int abs(const unsigned short a) { + return (int)a; } - inline unsigned int abs(const unsigned int a) { - return a; + inline int abs(const unsigned int a) { + return (int)a; } - inline unsigned long abs(const unsigned long a) { - return 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); @@ -4222,1010 +5259,1154 @@ namespace cimg_library { inline float abs(const float a) { return (float)std::fabs((double)a); } - inline int abs(const int a) { - return std::abs(a); - } - //! Get the square of a number. + //! Return square of a value. template - inline T sqr(const T val) { + inline T sqr(const T& val) { return val*val; } - //! Get 1 + log_10(x). + //! Return 1 + log_10(x) of a value \c x. inline int xln(const int x) { - return x>0?(int)(1+std::log10((double)x)):1; + return x>0?(int)(1 + std::log10((double)x)):1; } - //! Get the minimum value between two numbers. - template - inline typename cimg::superset::type min(const t1& a, const t2& b) { - typedef typename cimg::superset::type t1t2; - return (t1t2)(a<=b?a:b); + //! Return the minimum between three values. + template + inline t min(const t& a, const t& b, const t& c) { + return std::min(std::min(a,b),c); } - //! Get the minimum value between three numbers. - template - inline typename cimg::superset2::type min(const t1& a, const t2& b, const t3& c) { - typedef typename cimg::superset2::type t1t2t3; - return (t1t2t3)cimg::min(cimg::min(a,b),c); + //! Return the minimum between four values. + template + 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)); } - //! Get the minimum value between four numbers. - template - inline typename cimg::superset3::type min(const t1& a, const t2& b, const t3& c, const t4& d) { - typedef typename cimg::superset3::type t1t2t3t4; - return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d); + //! Return the maximum between three values. + template + inline t max(const t& a, const t& b, const t& c) { + return std::max(std::max(a,b),c); } - //! Get the maximum value between two numbers. - template - inline typename cimg::superset::type max(const t1& a, const t2& b) { - typedef typename cimg::superset::type t1t2; - return (t1t2)(a>=b?a:b); + //! Return the maximum between four values. + template + 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)); } - //! Get the maximum value between three numbers. - template - inline typename cimg::superset2::type max(const t1& a, const t2& b, const t3& c) { - typedef typename cimg::superset2::type t1t2t3; - return (t1t2t3)cimg::max(cimg::max(a,b),c); - } - - //! Get the maximum value between four numbers. - template - inline typename cimg::superset3::type max(const t1& a, const t2& b, const t3& c, const t4& d) { - typedef typename cimg::superset3::type t1t2t3t4; - return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d); - } - - //! Get the sign of a number. + //! Return the sign of a value. template - inline T sign(const T x) { - return (x<0)?(T)(-1):(x==0?(T)0:(T)1); + inline T sign(const T& x) { + return (T)(x<0?-1:x>0); } - //! Get the nearest power of 2 higher than a given number. + //! Return the nearest power of 2 higher than given value. template - inline unsigned int nearest_pow2(const T x) { - unsigned int i = 1; + inline cimg_ulong nearest_pow2(const T& x) { + cimg_ulong i = 1; while (x>i) i<<=1; return i; } - //! Get the sinc() of a given number. + //! Return the sinc of a given value. inline double sinc(const double x) { return x?std::sin(x)/x:1; } - //! Get the modulo of a number. + //! Return the modulo of a value. /** - \note This modulo function accepts negative and floating-points modulo numbers, as well as - variable of any type. + \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 inline T mod(const T& x, const T& m) { const double dx = (double)x, dm = (double)m; - if (x<0) { return (T)(dm+dx+dm*std::floor(-dx/dm)); } - return (T)(dx-dm*std::floor(dx/dm)); + 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 char x, const char m) { - return x>=0?x%m:(x%m?m+x%m:0); - } - inline int mod(const short x, const short m) { - return x>=0?x%m:(x%m?m+x%m:0); - } - inline int mod(const int x, const int m) { - return x>=0?x%m:(x%m?m+x%m:0); - } - inline int mod(const long x, const long m) { - return x>=0?x%m:(x%m?m+x%m: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 int mod(const unsigned long x, const unsigned long 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); } - //! Get the minmod of two numbers. + //! Return the min-mod of two values. /** - minmod(\p a,\p b) is defined to be : + \note minmod(\p a,\p b) is defined to be: - minmod(\p a,\p b) = min(\p a,\p b), if \p a and \p b have the same sign. - minmod(\p a,\p b) = 0, if \p a and \p b have different signs. **/ template - inline T minmod(const T a, const T b) { + inline T minmod(const T& a, const T& b) { return a*b<=0?0:(a>0?(a + inline T round(const T& x) { + return (T)std::floor((_cimg_Tfloat)x + 0.5f); } - //! Get a random variable following a gaussian distribution and a standard deviation of 1. - inline double grand() { - double x1, w; - do { - const double x2 = 2*cimg::rand() - 1.0; - x1 = 2*cimg::rand()-1.0; - w = x1*x1 + x2*x2; - } while (w<=0 || w>=1.0); - return x1*std::sqrt((-2*std::log(w))/w); - } - - //! Get a random variable following a Poisson distribution of parameter z. - inline unsigned int prand(const double z) { - if (z<=1.0e-10) return 0; - if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z); - unsigned int k = 0; - const double y = std::exp(-z); - for (double s = 1.0; s>=y; ++k) s*=cimg::rand(); - return k-1; - } - - //! Get a rounded number. + //! Return rounded value. /** - \param x : the number to be rounded. - \param y : the rounding precision. - \param rounding_type : the type of rounding (0=nearest, -1=backward, 1=forward). - \return the rounded value, with the same type as parameter x. + \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 - inline T round(const T x, const double y=1, const int rounding_type=0) { + inline T round(const T& x, const double y, const int rounding_type=0) { if (y<=0) return x; - const double delta = cimg::mod((double)x,y); - if (delta==0.0) return x; - const double backward = x - delta, forward = backward + y; - return (T)(rounding_type<0?backward:rounding_type>0?forward:2*deltay?forward:x<0?backward:forward); + if (y==1) switch (rounding_type) { + case 0 : return 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))); } - inline double _pythagore(double a, double b) { - const double absa = cimg::abs(a), absb = cimg::abs(b); - if (absa>absb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); } - else { const double tmp = absa/absb; return (absb==0?0:absb*std::sqrt(1.0+tmp*tmp)); } + //! Return x^(1/3). + template + inline double cbrt(const T& x) { +#if cimg_use_cpp11==1 + return std::cbrt(x); +#else + return x>=0?std::pow((double)x,1.0/3):-std::pow(-(double)x,1.0/3); +#endif } - //! Remove the 'case' of an ASCII character. - inline char uncase(const char x) { - return (char)((x<'A'||x>'Z')?x:x-'A'+'a'); + // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values. + // (contribution by RawTherapee: http://rawtherapee.com/). + template + inline T median(T val0, T val1) { + return (val0 + val1)/2; } - //! Remove the 'case' of a C string. + template + inline T median(T val0, T val1, T val2) { + return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1))); + } + + template + 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 + 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 + 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 + 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 + 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 + 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 + inline T hypot(const T x, const T y) { + return std::sqrt(x*x + y*y); + } + + template + inline T hypot(const T x, const T y, const T z) { + return std::sqrt(x*x + y*y + z*z); + } + + template + 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 (nx0) { 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::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::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.0))/2, + result = 1/std::sqrt(5.0); + 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::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)1304969544928657U, + fn2 = (cimg_uint64)806515533049393U, + 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 + } + + //! 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); + } + + //! Read value in a C-string. /** - Acts in-place. - **/ - inline void uncase(char *const string) { - if (string) for (char *ptr = string; *ptr; ++ptr) *ptr = uncase(*ptr); - } - - //! Read a double number from a C-string. - /** - \note This function is quite similar to std::atof(), - but that it allows the retrieval of fractions as in "1/2". + \param str C-string containing the float value to read. + \return Read value. + \note Same as std::atof() extended to manage the retrieval of fractions from C-strings, + as in "1/2". **/ inline double atof(const char *const str) { double x = 0, y = 1; - if (!str) return 0; else { std::sscanf(str,"%lf/%lf",&x,&y); return x/y; } + return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; } - //! Compare the first \p n characters of two C-strings, ignoring the case. + //! Compare the first \p l characters of two C-strings, ignoring the case. /** - \note This function is defined since it is not provided by all compilers - (not an ANSI function). + \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 s1, const char *const s2, const int l) { + inline int strncasecmp(const char *const str1, const char *const str2, const int l) { if (!l) return 0; - if (!s1) return s2?-1:0; - const char *ns1 = s1, *ns2 = s2; - int k, diff = 0; for (k = 0; kp && s[q]==delimiter; ) { --q; if (!is_iterative) break; } + 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; pp && str[q]==delimiter; ) { --q; if (!is_iterative) break; } } const int n = q - p + 1; - if (n!=l) { std::memmove(s,s+p,n); s[n] = 0; return true; } + if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } return false; } - //! Replace explicit escape sequences '\x' in C-strings. - inline void strescape(char *const s) { -#define cimg_strescape(ci,co) case ci: *nd = co; ++ns; break; - static unsigned int val = 0; - for (char *ns = s, *nd = s; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) { - cimg_strescape('n','\n'); - cimg_strescape('t','\t'); - cimg_strescape('v','\v'); - cimg_strescape('b','\b'); - cimg_strescape('r','\r'); - cimg_strescape('f','\f'); - cimg_strescape('a','\a'); - cimg_strescape('\\','\\'); - cimg_strescape('\?','\?'); - cimg_strescape('\'','\''); - cimg_strescape('\"','\"'); + //! 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; pp && (signed char)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' : - std::sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns; - *nd = val; break; - case 'x': - std::sscanf(++ns,"%x",&val); while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns; - *nd = val; break; + 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++); } - // Get a temporary string describing the size of a buffer. - inline const char *strbuffersize(const unsigned long size) { - static char res[256] = { 0 }; - if (size<1024LU) cimg_snprintf(res,sizeof(res),"%lu byte%s",size,size>1?"s":""); - else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,sizeof(res),"%.1f Kb",nsize); } - else if (size<1024*1024*1024LU) { const float nsize = size/(1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Mb",nsize); } - else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Gb",nsize); } - return res; + // 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; } - //! Compute the basename of a filename. - inline const char* basename(const char *const s) { - const char *p = 0; - for (const char *np = s; np>=s && (p=np); np = std::strchr(np,cimg_file_separator)+1) {} + //! 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; } - // Generate a random filename. + // Return a random filename. inline const char* filenamerand() { - static char randomid[9] = { 0,0,0,0,0,0,0,0,0 }; + cimg::mutex(6); + static char randomid[9]; cimg::srand(); for (unsigned int k = 0; k<8; ++k) { - const int v = (int)std::rand()%3; - randomid[k] = (char)(v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26)))); + 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 into a Windows-style filename. - inline void winformat_string(char *const s) { - if (s && *s) { + // 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 ns = new char[MAX_PATH]; - if (GetShortPathNameA(s,ns,MAX_PATH)) std::strcpy(s,ns); + char *const nstr = new char[MAX_PATH]; + if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr); + delete[] nstr; #endif } } - //! Open a file, and check for possible errors. + // Open a file (with wide character support on Windows). + inline std::FILE *win_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 std::fopen() 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)."); + throw CImgArgumentException("cimg::fopen(): Specified file path is (null)."); if (!mode) - throw CImgArgumentException("cimg::fopen() : File '%s', specified mode is (null).", + throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).", path); std::FILE *res = 0; - if (*path=='-' && path[1]=='.') { - res = (*mode=='r')?stdin:stdout; + 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 = std::fopen(path,mode); - if (!res) throw CImgIOException("cimg::fopen() : Failed to open file '%s' with mode '%s'.", + } else res = 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, and check for possible errors. + //! Close a file. + /** + \param file File to close. + \return \c 0 if file has been closed properly, something else otherwise. + \note Same as std::fclose() 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)."); - if (!file || file==stdin || file==stdout) return 0; + 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.", + if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.", errn); return errn; } - //! Get or set path to store temporary files. - inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) { -#define _cimg_test_temporary_path(p) \ - if (!path_found) { \ - cimg_snprintf(st_path,1024,"%s",p); \ - cimg_snprintf(tmp,sizeof(tmp),"%s%c%s",st_path,cimg_file_separator,filetmp); \ - if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ - } - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,1024); - bool path_found = false; - char tmp[1024] = { 0 }, filetmp[512] = { 0 }; - std::FILE *file = 0; - cimg_snprintf(filetmp,sizeof(filetmp),"%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); + //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows). + inline int fseek(FILE *stream, cimg_long offset, int origin) { #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:"); + return _fseeki64(stream,(__int64)offset,origin); #else - _cimg_test_temporary_path("/tmp"); - _cimg_test_temporary_path("/var/tmp"); + return std::fseek(stream,offset,origin); #endif - if (!path_found) { - *st_path = 0; - std::strncpy(tmp,filetmp,sizeof(tmp)-1); - if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } - } - if (!path_found) - throw CImgIOException("cimg::temporary_path() : Failed to locate path for writing temporary files.\n"); - } - return st_path; } - // Get or set path to the "Program files/" directory (windows only). + //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows). + inline cimg_long ftell(FILE *stream) { #if cimg_OS==2 - inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[MAX_PATH]; - std::memset(st_path,0,MAX_PATH); - // Note : in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). -#if !defined(__INTEL_COMPILER) - if (!SHGetSpecialFolderPathA(0,st_path,0x0026,false)) { - const char *const pfPath = std::getenv("PROGRAMFILES"); - if (pfPath) std::strncpy(st_path,pfPath,MAX_PATH-1); - else std::strcpy(st_path,"C:\\PROGRA~1"); - } + return (cimg_long)_ftelli64(stream); #else - std::strcpy(st_path,"C:\\PROGRA~1"); + return (cimg_long)std::ftell(stream); #endif - } - return st_path; - } -#endif - - //! Get or set path to the ImageMagick's \c convert tool. - inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,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(st_path,".\\convert.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"convert.exe"); -#else - if (!path_found) { - std::strcpy(st_path,"./convert"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"convert"); -#endif - winformat_string(st_path); - } - return st_path; } - //! Get path of the GraphicsMagick's \c gm tool. - inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,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(st_path,".\\gm.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=10 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 9; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - for (int k = 32; k>=0 && !path_found; --k) { - cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"gm.exe"); + //! 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 - if (!path_found) { - std::strcpy(st_path,"./gm"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"gm"); + return false; #endif - winformat_string(st_path); - } - return st_path; } - //! Get or set path of the \c XMedcon tool. - inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,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(st_path,".\\medcon.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.bat",pf_path); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) { - cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.exe",pf_path); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"medcon.exe"); -#else - if (!path_found) { - std::strcpy(st_path,"./medcon"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"medcon"); -#endif - winformat_string(st_path); - } - return st_path; + //! 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 = std_fopen(path,"rb"); + if (!file) return false; + std::fclose(file); + return !is_directory(path); } - //! Get or set path to the 'ffmpeg' command. - inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,1024); - bool path_found = false; - std::FILE *file = 0; + //! Get last write time of a given file or directory. + /** + \param path Specified path to get attributes from. + \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 -1 if requested attribute could not be read. + **/ + inline int fdate(const char *const path, const unsigned int attr) { + int res = -1; + if (!path || !*path || attr>6) return -1; + cimg::mutex(6); #if cimg_OS==2 - if (!path_found) { - std::strcpy(st_path,".\\ffmpeg.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"ffmpeg.exe"); -#else - if (!path_found) { - std::strcpy(st_path,"./ffmpeg"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"ffmpeg"); -#endif - winformat_string(st_path); + 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)) + res = (int)(attr==0?ft.wYear:attr==1?ft.wMonth:attr==2?ft.wDay:attr==3?ft.wDayOfWeek: + attr==4?ft.wHour:attr==5?ft.wMinute:ft.wSecond); + CloseHandle(file); } - return st_path; +#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); + res = (int)(attr==0?ft.tm_year + 1900:attr==1?ft.tm_mon + 1:attr==2?ft.tm_mday:attr==3?ft.tm_wday: + attr==4?ft.tm_hour:attr==5?ft.tm_min:ft.tm_sec); + } +#endif + cimg::mutex(6,0); + return res; } - //! Get or set path to the 'gzip' command. - inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,1024); - bool path_found = false; - std::FILE *file = 0; + //! Get current local time. + /** + \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 } + **/ + inline int date(const unsigned int attr) { + int res; + cimg::mutex(6); #if cimg_OS==2 - if (!path_found) { - std::strcpy(st_path,".\\gzip.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"gzip.exe"); + SYSTEMTIME st; + GetLocalTime(&st); + res = (int)(attr==0?st.wYear:attr==1?st.wMonth:attr==2?st.wDay:attr==3?st.wDayOfWeek: + attr==4?st.wHour:attr==5?st.wMinute:st.wSecond); #else - if (!path_found) { - std::strcpy(st_path,"./gzip"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"gzip"); + time_t _st; + std::time(&_st); + struct tm *st = std::localtime(&_st); + res = (int)(attr==0?st->tm_year + 1900:attr==1?st->tm_mon + 1:attr==2?st->tm_mday:attr==3?st->tm_wday: + attr==4?st->tm_hour:attr==5?st->tm_min:st->tm_sec); #endif - winformat_string(st_path); - } - return st_path; + cimg::mutex(6,0); + return res; } - //! Get or set path to the 'gunzip' command. - inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(st_path,".\\gunzip.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"gunzip.exe"); -#else - if (!path_found) { - std::strcpy(st_path,"./gunzip"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"gunzip"); -#endif - winformat_string(st_path); - } - return st_path; - } + // Get/set path to store temporary files. + inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false); - //! Get or set path to the 'dcraw' command. - inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,1024); - bool path_found = false; - std::FILE *file = 0; + // Get/set path to the Program Files/ directory (Windows only). #if cimg_OS==2 - if (!path_found) { - std::strcpy(st_path,".\\dcraw.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"dcraw.exe"); -#else - if (!path_found) { - std::strcpy(st_path,"./dcraw"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"dcraw"); + inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false); #endif - winformat_string(st_path); - } - return st_path; - } - //! Get or set path to the 'wget' command. - inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(st_path,".\\wget.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"wget.exe"); -#else - if (!path_found) { - std::strcpy(st_path,"./wget"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"wget"); -#endif - winformat_string(st_path); - } - return st_path; - } + // 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 or set path to the 'curl' command. - inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false) { - static char *st_path = 0; - if (reinit_path) { delete[] st_path; st_path = 0; } - if (user_path) { - if (!st_path) st_path = new char[1024]; - std::memset(st_path,0,1024); - std::strncpy(st_path,user_path,1023); - } else if (!st_path) { - st_path = new char[1024]; - std::memset(st_path,0,1024); - bool path_found = false; - std::FILE *file = 0; -#if cimg_OS==2 - if (!path_found) { - std::strcpy(st_path,".\\curl.exe"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"curl.exe"); -#else - if (!path_found) { - std::strcpy(st_path,"./curl"); - if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(st_path,"curl"); -#endif - winformat_string(st_path); - } - return st_path; - } + // 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); - //! Split a filename into two strings 'body' and 'extension'. + // 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) {} + 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 = p - filename - 1; - if (body) { std::memcpy(body,filename,l); body[l] = 0; } + const unsigned int l = (unsigned int)(p - filename - 1); + if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; } return p; } - //! Create a numbered version of a filename. - inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const string) { - if (!filename) { if (string) *string = 0; return 0; } - char format[1024] = { 0 }, body[1024] = { 0 }; + //! 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 (n>0) cimg_snprintf(format,sizeof(format),"%s_%%.%ud.%s",body,n,ext); - else cimg_snprintf(format,sizeof(format),"%s_%%d.%s",body,ext); - std::sprintf(string,format,number); - return string; + 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; } - //! Try to guess the image format of a filename, using the magic numbers in its header. - inline const char *file_type(std::FILE *const file, const char *const filename) { - if (!file && !filename) - throw CImgArgumentException("cimg::file_type() : 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"; - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - const char *f_type = 0, *head; - char header[2048] = { 0 }, item[1024] = { 0 }; - const unsigned char *const uheader = (unsigned char*)header; - int err; char cerr; - const unsigned int siz = (unsigned int)std::fread(header,2048,1,nfile); // Read first 2048 bytes. - if (!file) cimg::fclose(nfile); - - if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // Check for OFF format. - else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // Check for INRIMAGE format. - else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // Check for PANDORE format. - else if (!std::strncmp(header+128,"DICM",4)) f_type = _dcm; // Check for DICOM format. - else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg; // Check for JPEG format. - else if (header[0]=='B' && header[1]=='M') f_type = _bmp; // Check for BMP format. - else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // Check for GIF format. - (header[4]=='7' || header[4]=='9')) f_type = _gif; - else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && // Check for PNG format. - 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; // Check for TIFF format. - else { // Check for PNM or PFM format. - head = header; - while (headstd::fread() but may display warning message if all elements could not be read. + **/ template - inline int fread(T *const ptr, const unsigned int nmemb, std::FILE *stream) { - if (!ptr || nmemb<=0 || !stream) - throw CImgArgumentException("cimg::fread() : Invalid reading request of %u %s%s from file %p to buffer %p.", + 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::string(),nmemb>1?"s":"",stream,ptr); - - const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); - unsigned int to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0; + 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))0); if (to_read>0) - warn("cimg::fread() : Only %u/%u elements could be read from file.", - al_read,nmemb); + 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 a file, and check for possible errors. + //! 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 std::fwrite but may display warning messages if all elements could not be written. + **/ template - inline int fwrite(const T *ptr, const unsigned int nmemb, std::FILE *stream) { + 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.", + throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.", nmemb,cimg::type::string(),nmemb>1?"s":"",ptr,stream); - if (nmemb<=0) return 0; - const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); - unsigned int to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0; + 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))0); if (to_write>0) - warn("cimg::fwrite() : Only %u/%u elements could be written in file.", - al_write,nmemb); + warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.", + (unsigned long)al_write,(unsigned long)nmemb); return al_write; } - //! Load file from network as a local temporary file, using 'wget' or 'curl' if found. - inline char *load_network_external(const char *const filename, char *const filename_local) { - if (!filename) - throw CImgArgumentException("cimg::load_network_external() : Specified filename is (null)."); - if (!filename_local) - throw CImgArgumentException("cimg::load_network_external() : Specified destination string is (null)."); - const char *const _ext = cimg::split_filename(filename), *const ext = (*_ext && _ext>filename)?_ext-1:_ext; - char command[1024] = { 0 }; - std::FILE *file = 0; - *filename_local = 0; - do { - cimg_snprintf(filename_local,512,"%s%c%s%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - if ((file=std::fopen(filename_local,"rb"))!=0) cimg::fclose(file); - } while (file); - - // Try with 'curl' first. - cimg_snprintf(command,sizeof(command),"%s -f --silent --compressed -o \"%s\" \"%s\"",cimg::curl_path(),filename_local,filename); - cimg::system(command); - if (!(file = std::fopen(filename_local,"rb"))) { - - // Try with 'wget' else. - cimg_snprintf(command,sizeof(command),"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",cimg::wget_path(),filename_local,filename); - cimg::system(command); - if (!(file = std::fopen(filename_local,"rb"))) - throw CImgIOException("cimg::load_network_external() : Failed to load file '%s' with external tools 'wget' or 'curl'.",filename); - cimg::fclose(file); - - // Try gunzip it. - cimg_snprintf(command,sizeof(command),"%s.gz",filename_local); - std::rename(filename_local,command); - cimg_snprintf(command,sizeof(command),"%s --quiet %s.gz",gunzip_path(),filename_local); - cimg::system(command); - file = std::fopen(filename_local,"rb"); - if (!file) { - cimg_snprintf(command,sizeof(command),"%s.gz",filename_local); - std::rename(command,filename_local); - file = std::fopen(filename_local,"rb"); - } - } - std::fseek(file,0,SEEK_END); // Check if file size is 0. - if (!(unsigned int)std::ftell(file)) - throw CImgIOException("cimg::load_network_external() : Failed to load file '%s' with external commands 'wget' or 'curl'.",filename); - cimg::fclose(file); - return filename_local; + //! 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; @@ -5240,8 +6421,8 @@ namespace cimg_library { 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",__DATE__,__TIME__); + 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); } @@ -5252,7 +6433,8 @@ namespace cimg_library { 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); + cimg::t_bold,name,cimg::t_normal,res?res:"0", + cimg::t_green,usage,cimg::t_normal); } return res; } @@ -5274,9 +6456,10 @@ namespace cimg_library { 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 tmp[256] = { 0 }; - cimg_snprintf(tmp,sizeof(tmp),"%d",res); + char *const tmp = new char[256]; + cimg_snprintf(tmp,256,"%d",res); cimg::option(name,0,0,tmp,usage); + delete[] tmp; return res; } @@ -5284,8 +6467,8 @@ namespace cimg_library { 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] = { 0 }; - *tmp = res; + char tmp[8]; + *tmp = res; tmp[1] = 0; cimg::option(name,0,0,tmp,usage); return res; } @@ -5294,9 +6477,10 @@ namespace cimg_library { 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 tmp[256] = { 0 }; - cimg_snprintf(tmp,sizeof(tmp),"%g",res); + char *const tmp = new char[256]; + cimg_snprintf(tmp,256,"%g",res); cimg::option(name,0,0,tmp,usage); + delete[] tmp; return res; } @@ -5304,58 +6488,45 @@ namespace cimg_library { 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 tmp[256] = { 0 }; - cimg_snprintf(tmp,sizeof(tmp),"%g",res); + char *const tmp = new char[256]; + cimg_snprintf(tmp,256,"%g",res); cimg::option(name,0,0,tmp,usage); + delete[] tmp; return res; } - inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv, const unsigned int nb_singles=0, ...) { - for (int k = 1, pos = 0; k Operating System : %s%-13s%s %s('cimg_OS'=%d)%s\n", + 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", + 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", + 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_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", + 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", @@ -5364,7 +6535,14 @@ namespace cimg_library { #endif cimg::t_normal); - std::fprintf(cimg::output()," > Using VT100 messages : %s%-13s%s %s('cimg_use_vt100' %s)%s\n", + 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", @@ -5373,15 +6551,15 @@ namespace cimg_library { #endif cimg::t_normal); - std::fprintf(cimg::output()," > Display type : %s%-13s%s %s('cimg_display'=%d)%s\n", + 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, - cimg_display, + (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", + 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", @@ -5390,7 +6568,7 @@ namespace cimg_library { #endif cimg::t_normal); - std::fprintf(cimg::output()," > Using XRand for X11 : %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", + 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", @@ -5399,7 +6577,7 @@ namespace cimg_library { #endif cimg::t_normal); #endif - std::fprintf(cimg::output()," > Using OpenMP : %s%-13s%s %s('cimg_use_openmp' %s)%s\n", + 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", @@ -5407,7 +6585,7 @@ namespace cimg_library { "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", + 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", @@ -5415,7 +6593,7 @@ namespace cimg_library { "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", + 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", @@ -5424,7 +6602,7 @@ namespace cimg_library { #endif cimg::t_normal); - std::fprintf(cimg::output()," > Using TIFF library : %s%-13s%s %s('cimg_use_tiff' %s)%s\n", + 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", @@ -5433,7 +6611,7 @@ namespace cimg_library { #endif cimg::t_normal); - std::fprintf(cimg::output()," > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n", + 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", @@ -5442,7 +6620,7 @@ namespace cimg_library { #endif cimg::t_normal); - std::fprintf(cimg::output()," > Using FFTW3 library : %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", + 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", @@ -5451,7 +6629,7 @@ namespace cimg_library { #endif cimg::t_normal); - std::fprintf(cimg::output()," > Using LAPACK library : %s%-13s%s %s('cimg_use_lapack' %s)%s\n", + 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", @@ -5460,34 +6638,36 @@ namespace cimg_library { #endif cimg::t_normal); - cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::imagemagick_path()); - std::fprintf(cimg::output()," > Path of ImageMagick : %s%-13s%s\n", + 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,sizeof(tmp),"\"%.1020s\"",cimg::graphicsmagick_path()); - std::fprintf(cimg::output()," > Path of GraphicsMagick : %s%-13s%s\n", + 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,sizeof(tmp),"\"%.1020s\"",cimg::medcon_path()); - std::fprintf(cimg::output()," > Path of 'medcon' : %s%-13s%s\n", + 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,sizeof(tmp),"\"%.1020s\"",cimg::temporary_path()); - std::fprintf(cimg::output()," > Temporary path : %s%-13s%s\n", + 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 necessary. + // Declare LAPACK function signatures if LAPACK support is enabled. #ifdef cimg_use_lapack template inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { @@ -5537,6 +6717,18 @@ namespace cimg_library { 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 + 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 @@ -5581,6 +6773,14 @@ namespace cimg_library { inline CImg::type> operator^(const typ val, const CImg& img) { \ return img ^ val; \ } \ + template \ + inline bool operator==(const typ val, const CImg& img) { \ + return img == val; \ + } \ + template \ + inline bool operator!=(const typ val, const CImg& img) { \ + return img != val; \ + } _cimg_create_ext_operators(bool) _cimg_create_ext_operators(unsigned char) @@ -5590,10 +6790,11 @@ namespace cimg_library { _cimg_create_ext_operators(short) _cimg_create_ext_operators(unsigned int) _cimg_create_ext_operators(int) - _cimg_create_ext_operators(unsigned long) - _cimg_create_ext_operators(long) + _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 inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg& img) { @@ -5602,7 +6803,7 @@ namespace cimg_library { template inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg& img) { - return (CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum)=expression)-=img; + return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img; } template @@ -5630,6 +6831,16 @@ namespace cimg_library { return img ^ expression; } + template + inline bool operator==(const char *const expression, const CImg& img) { + return img==expression; + } + + template + inline bool operator!=(const char *const expression, const CImg& img) { + return img!=expression; + } + template inline CImg<_cimg_Tfloat> sqr(const CImg& instance) { return instance.get_sqr(); @@ -5650,6 +6861,11 @@ namespace cimg_library { return instance.get_log(); } + template + inline CImg<_cimg_Tfloat> log2(const CImg& instance) { + return instance.get_log2(); + } + template inline CImg<_cimg_Tfloat> log10(const CImg& instance) { return instance.get_log10(); @@ -5730,175 +6946,49 @@ namespace cimg_library { return instance.get_pseudoinvert(); } - /*------------------------------------------- + /*----------------------------------- # + # Define the CImgDisplay structure # - # - # Definition of the CImgDisplay structure - # - # - # - --------------------------------------------*/ - - //! This class represents a window which can display \c CImg images and handles mouse and keyboard events. + ----------------------------------*/ + //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events). /** - Creating a \c CImgDisplay instance opens a window that can be used to display a \c CImg image - of a \c CImgList image list inside. When a display is created, associated window events - (such as mouse motion, keyboard and window size changes) are handled and can be easily - detected by testing specific \c CImgDisplay data fields. - See \ref cimg_displays for a complete tutorial on using the \c CImgDisplay class. + 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 { - - //! Width of the display. - unsigned int _width; - - //! Height of the display. - unsigned int _height; - - //! Width of the underlying window. - volatile unsigned int _window_width; - - //! Height of the underlying window. - volatile unsigned int _window_height; - - //! X-pos of the display on the screen. - volatile int _window_x; - - //! Y-pos of the display on the screen. - volatile int _window_y; - - //! X-coordinate of the mouse pointer on the display. - volatile int _mouse_x; - - //! Y-coordinate of the mouse pointer on the display. - volatile int _mouse_y; - - //! Normalization type used for the display. - unsigned int _normalization; - - //! Display title. - char *_title; - - //! Button state of the mouse. - volatile unsigned int _button; - - //! Wheel state of the mouse. - volatile int _wheel; - - //! Key value if pressed. - volatile unsigned int _keys[128]; - volatile unsigned int _released_keys[128]; - - //! Closed state of the window. - volatile bool _is_closed; - - //! Resized state of the window. - volatile bool _is_resized; - - //! Moved state of the window. - volatile bool _is_moved; - - //! Event state of the window. - volatile bool _is_event; - - //! Current state of the corresponding key (exists for all referenced keys). - volatile bool _is_keyESC; - volatile bool _is_keyF1; - volatile bool _is_keyF2; - volatile bool _is_keyF3; - volatile bool _is_keyF4; - volatile bool _is_keyF5; - volatile bool _is_keyF6; - volatile bool _is_keyF7; - volatile bool _is_keyF8; - volatile bool _is_keyF9; - volatile bool _is_keyF10; - volatile bool _is_keyF11; - volatile bool _is_keyF12; - volatile bool _is_keyPAUSE; - volatile bool _is_key1; - volatile bool _is_key2; - volatile bool _is_key3; - volatile bool _is_key4; - volatile bool _is_key5; - volatile bool _is_key6; - volatile bool _is_key7; - volatile bool _is_key8; - volatile bool _is_key9; - volatile bool _is_key0; - volatile bool _is_keyBACKSPACE; - volatile bool _is_keyINSERT; - volatile bool _is_keyHOME; - volatile bool _is_keyPAGEUP; - volatile bool _is_keyTAB; - volatile bool _is_keyQ; - volatile bool _is_keyW; - volatile bool _is_keyE; - volatile bool _is_keyR; - volatile bool _is_keyT; - volatile bool _is_keyY; - volatile bool _is_keyU; - volatile bool _is_keyI; - volatile bool _is_keyO; - volatile bool _is_keyP; - volatile bool _is_keyDELETE; - volatile bool _is_keyEND; - volatile bool _is_keyPAGEDOWN; - volatile bool _is_keyCAPSLOCK; - volatile bool _is_keyA; - volatile bool _is_keyS; - volatile bool _is_keyD; - volatile bool _is_keyF; - volatile bool _is_keyG; - volatile bool _is_keyH; - volatile bool _is_keyJ; - volatile bool _is_keyK; - volatile bool _is_keyL; - volatile bool _is_keyENTER; - volatile bool _is_keySHIFTLEFT; - volatile bool _is_keyZ; - volatile bool _is_keyX; - volatile bool _is_keyC; - volatile bool _is_keyV; - volatile bool _is_keyB; - volatile bool _is_keyN; - volatile bool _is_keyM; - volatile bool _is_keySHIFTRIGHT; - volatile bool _is_keyARROWUP; - volatile bool _is_keyCTRLLEFT; - volatile bool _is_keyAPPLEFT; - volatile bool _is_keyALT; - volatile bool _is_keySPACE; - volatile bool _is_keyALTGR; - volatile bool _is_keyAPPRIGHT; - volatile bool _is_keyMENU; - volatile bool _is_keyCTRLRIGHT; - volatile bool _is_keyARROWLEFT; - volatile bool _is_keyARROWDOWN; - volatile bool _is_keyARROWRIGHT; - volatile bool _is_keyPAD0; - volatile bool _is_keyPAD1; - volatile bool _is_keyPAD2; - volatile bool _is_keyPAD3; - volatile bool _is_keyPAD4; - volatile bool _is_keyPAD5; - volatile bool _is_keyPAD6; - volatile bool _is_keyPAD7; - volatile bool _is_keyPAD8; - volatile bool _is_keyPAD9; - volatile bool _is_keyPADADD; - volatile bool _is_keyPADSUB; - volatile bool _is_keyPADMUL; - volatile bool _is_keyPADDIV; - - //! Fullscreen state of the display. - bool _is_fullscreen; - - // Internal variables. + cimg_ulong _timer, _fps_frames, _fps_timer; + unsigned int _width, _height, _normalization; float _fps_fps, _min, _max; - unsigned long _timer, _fps_frames, _fps_timer; + 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; //@} //--------------------------- @@ -5943,144 +7033,230 @@ namespace cimg_library { //-------------------------------------------------------- //! 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; } - //! Create an empty display window. + //! 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),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1), - _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false), - _is_fullscreen(false),_min(0),_max(0) { + _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(); } - //! Create a display window with a specified size \p pwidth x \p height. - /** \param width : Width of the display window. - \param height : Height of the display window. - \param title : Title of the display window. - \param normalization : Normalization type of the display window (0=none, 1=always, 2=once). - \param is_fullscreen : Fullscreen mode. - \param is_closed : Initially visible mode. - A black image will be initially displayed in the display window. + //! Construct a display with specified dimensions. + /** \param width Window width. + \param height Window height. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=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),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1), - _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false), - _is_fullscreen(false),_min(0),_max(0) { + _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); } - //! Create a display window from an image. - /** \param img : Image that will be used to create the display window. - \param title : Title of the display window - \param normalization : Normalization type of the display window. - \param is_fullscreen : Fullscreen mode. - \param is_closed : Initially visible mode. + //! 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 + (0=none, 1=always, 2=once, 3=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 explicit CImgDisplay(const CImg& 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),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1), - _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false), - _is_fullscreen(false),_min(0),_max(0) { + _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); } - //! Create a display window from an image list. - /** \param list : The list of images to display. - \param title : Title of the display window - \param normalization : Normalization type of the display window. - \param is_fullscreen : Fullscreen mode. - \param is_closed : Initially visible mode. + //! Construct a display from an image list. + /** \param list The images list to display. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=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 explicit CImgDisplay(const CImgList& 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),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1), - _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false), - _is_fullscreen(false),_min(0),_max(0) { + _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); } - //! Create a display window by copying another one. + //! Construct a display as a copy of an existing one. /** - \param disp : Display window to copy. + \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),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1), - _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false), - _is_fullscreen(false),_min(0),_max(0) { + _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 + static void screenshot(CImg& img) { + return screenshot(0,0,cimg::type::max(),cimg::type::max(),img); + } + #if cimg_display==0 static void _no_display_exception() { - throw CImgDisplayException("CImgDisplay() : No display available."); + throw CImgDisplayException("CImgDisplay(): No display available."); } - //! In-place version of the destructor. + //! Destructor - Empty constructor \inplace. + /** + \note Replace the current instance by an empty display. + **/ CImgDisplay& assign() { - _no_display_exception(); return flush(); } - //! In-place version of the constructor. + //! 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(); } - //! In-place version of the constructor. + //! Construct a display from an image \inplace. + /** + **/ template CImgDisplay& assign(const CImg& 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); } - //! In-place version of the constructor. + //! Construct a display from an image list \inplace. + /** + **/ template CImgDisplay& assign(const CImgList& 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); } - //! In-place version of the constructor. + //! 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 - //! Get a reference to an empty display. + //! 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(); } -#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false),CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true) + //! 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,128,-85,false), \ + CImgDisplay::_fitscreen(dx,dy,dz,128,-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) { - unsigned int nw = dx + (dz>1?dz:0), nh = dy + (dz>1?dz:0); + const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0); + unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1; const unsigned int - sw = CImgDisplay::screen_width(), sh = CImgDisplay::screen_height(), + sw = (unsigned int)CImgDisplay::screen_width(), + sh = (unsigned int)CImgDisplay::screen_height(), mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin, mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin, Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax, Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax; - if (nwMw) { nh = nh*Mw/nw; nh+=(nh==0?1:0); nw = Mw; } - if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0?1:0); nh = Mh; } + if (nwMw) { nh = nh*Mw/nw; nh+=(nh==0); nw = Mw; } + if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0); nh = Mh; } if (nwdisp = img is equivalent to disp.display(img). + **/ template CImgDisplay& operator=(const CImg& img) { return display(img); } - // Operator=(). + //! Display list of images on associated window. + /** + \note disp = list is equivalent to disp.display(list). + **/ template CImgDisplay& operator=(const CImgList& list) { return display(list); } - //! Operator=(). + //! 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 true if display is not empty. + //! Return \c false if display is empty, \c true otherwise. + /** + \note if (disp) { ... } is equivalent to if (!disp.is_empty()) { ... }. + **/ operator bool() const { return !is_empty(); } @@ -6122,31 +7310,55 @@ namespace cimg_library { //@{ //------------------------------------------ - //! Return true is display is empty. + //! 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 || @@ -6175,38 +7387,22 @@ namespace cimg_library { _is_keyPADMUL || _is_keyPADDIV; } -#define _cimg_iskey_def(k) \ - bool is_key##k() const { \ - return _is_key##k; \ - } - _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); - - bool is_key(const unsigned int key) const { -#define _cimg_iskey_test(k) if (key==cimg::key##k) return _is_key##k; + //! 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); @@ -6235,9 +7431,24 @@ namespace cimg_library { return false; } - //! Get keycode corresponding to given input string. - bool is_key(const char *const textcode) const { -#define _cimg_iskey_test2(k) if (!cimg::strcasecmp(textcode,#k)) return _is_key##k; + //! 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); @@ -6263,15 +7474,32 @@ namespace cimg_library { _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 false; + return f; } - //! Test if a key sequence has been typed. - bool is_key_sequence(const unsigned int *const key_sequence, const unsigned int length, const bool remove_sequence=false) { - if (key_sequence && length) { + //! 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 = key_sequence + length - 1, - *const pk_end = (unsigned int*)_keys + 1 + sizeof(_keys)/sizeof(unsigned int) - length, + *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[0,255]. + 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 [0,255]. + - \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 [0,255], 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 unsigned char). + 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; } - //! Get Y-coordinate of the mouse pointer. + //! 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; } - //! Get current or previous state of the mouse buttons. + //! 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; } - //! Get current state of the mouse wheel. + //! 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; } - //! Get current or previous state of the keyboard. + //! 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<(sizeof(_keys)/sizeof(unsigned int))?_keys[pos]:0; + 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<(sizeof(_released_keys)/sizeof(unsigned int))?_released_keys[pos]:0; + return pos<128?_released_keys[pos]:0; } - //! Get keycode corresponding to given input string. - static unsigned int keycode(const char *const textcode) { -#define _cimg_keycode(k) if (!cimg::strcasecmp(textcode,#k)) return cimg::key##k; + //! 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); @@ -6365,56 +7818,14 @@ namespace cimg_library { return 0; } - //! Get normalization type of the display. - unsigned int normalization() const { - return _normalization; - } - - //! Get title of the display. - const char *title() const { - return _title; - } - - //! Get display window width. - int window_width() const { - return (int)_window_width; - } - - //! Get display window height. - int window_height() const { - return (int)_window_height; - } - - //! Get X-coordinate of the window. - int window_x() const { - return _window_x; - } - - //! Get Y-coordinate of the window. - int window_y() const { - return _window_y; - } - -#if cimg_display==0 - - //! Get the width of the screen resolution. - static int screen_width() { - _no_display_exception(); - return 0; - } - - //! Get the height of the screen resolution. - static int screen_height() { - _no_display_exception(); - return 0; - } - -#endif - - //! Get the frame per second rate. + //! 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.0f; + const float delta = (cimg::time() - _fps_timer)/1000.0f; ++_fps_frames; if (delta>=1) { _fps_fps = _fps_frames/delta; @@ -6425,15 +7836,19 @@ namespace cimg_library { } //@} - //------------------------------------------ + //--------------------------------------- // - //! \name Display Manipulation + //! \name Window Manipulation //@{ - //------------------------------------------ + //--------------------------------------- #if cimg_display==0 - //! Display an image in a window. + //! Display image on associated window. + /** + \param img Input image to display. + \note This method returns immediately. + **/ template CImgDisplay& display(const CImg& img) { return assign(img); @@ -6441,59 +7856,139 @@ namespace cimg_library { #endif - //! Display an image list CImgList into a display window. - /** First, all images of the list are appended into a single image used for visualization, - then this image is displayed in the current display window. - \param list : The list of images to display. - \param axis : The axis used to append the image for visualization. Can be 'x' (default),'y','z' or 'c'. - \param align : Defines the relative alignment of images when displaying images of different sizes. - Can be '\p c' (centered, which is the default), '\p p' (top alignment) and '\p n' (bottom aligment). + //! 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 CImgDisplay& display(const CImgList& list, const char axis='x', const float align=0) { - return display(list.get_append(axis,align)); - } - - //! Resize a display window in its current size. - CImgDisplay& resize(const bool force_redraw=true) { - resize(_window_width,_window_height,force_redraw); + if (list._width==1) { + const CImg& img = list[0]; + if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img); + } + CImgList::ucharT> visu(list._width); + unsigned int dims = 0; + cimglist_for(list,l) { + const CImg& 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 - CImgDisplay& resize(const CImg& img, const bool force_redraw=true) { - return resize(img._width,img._height,force_redraw); - } - - //! Resize a display window using the size of the given display \p disp. - CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) { - return resize(disp._width,disp._height,force_redraw); - } - #if cimg_display==0 - //! Resize window. + //! 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 - // Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs). + //! 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 img.width() become equal, as well as height() and + img.height(). + - The associated window is also resized to specified dimensions. + **/ + template + CImgDisplay& resize(const CImg& 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 disp.width() become equal, as well as height() and + disp.height(). + - 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 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) { - unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy; + unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd + 1], *poffx, *poffy; float s, curr, old; s = (float)ws/wd; - poffx = offx; curr = 0; for (unsigned int x = 0; xstd::printf(). + \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); @@ -6517,110 +8054,147 @@ namespace cimg_library { #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 a closed display. - CImgDisplay& show() { - return assign(); - } - - //! Close a visible display. - CImgDisplay& close() { - return assign(); - } - - //! Move window. - CImgDisplay& move(const int pos_x, const int pos_y) { - return assign(pos_x,pos_y); - } - //! 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 specific location. + //! 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); } - CImgDisplay& set_title(const char *const format, ...) { - return assign(0,0,format); - } - - //! Render image buffer into GDI native image format. - template - CImgDisplay& render(const CImg& img) { - return assign(img); - } - - //! Re-paint image content in window. - CImgDisplay& paint() { - return assign(); - } - - //! Take a snapshot of the display in the specified image. - template - const CImgDisplay& snapshot(CImg& img) const { - _no_display_exception(); - return *this; - } #endif - //! Simulate a mouse button event. + //! 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==1?1:button==2?2:button==3?4:0; + 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; } - //! Simulate a mouse wheel event, or flush wheel events. + //! 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; } - //! Simulate a keyboard press/release event, or flush all key events. + //! Flush all key events. + /** + \note Make key() to return \c 0, if called afterwards. + **/ CImgDisplay& set_key() { - std::memset((void*)_keys,0,sizeof(_keys)); - std::memset((void*)_released_keys,0,sizeof(_released_keys)); - _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; + 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; } - CImgDisplay& set_key(const unsigned int keycode, const bool pressed=true) { -#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = pressed; + //! 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); @@ -6646,28 +8220,38 @@ namespace cimg_library { _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 (pressed) { + if (is_pressed) { if (*_keys) - std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int)); + 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,sizeof(_released_keys) - sizeof(unsigned int)); + 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,sizeof(_keys) - sizeof(unsigned int)); + 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,sizeof(_released_keys) - sizeof(unsigned int)); + 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; @@ -6676,57 +8260,63 @@ namespace cimg_library { return *this; } - //! Synchronized waiting function. Same as cimg::wait(). - CImgDisplay& wait(const unsigned int milliseconds) { - cimg::_sleep(milliseconds,_timer); - return *this; - } - - //! Wait for an event occuring on the current display. + //! 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 = 0; + 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 = 0; + 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 = 0; + 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 = 0; + 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 = 0; + 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(); + !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 = 0; + 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 && @@ -6737,7 +8327,7 @@ namespace cimg_library { 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 = 0; + 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 && @@ -6748,7 +8338,7 @@ namespace cimg_library { 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 = 0; + 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 && @@ -6759,7 +8349,7 @@ namespace cimg_library { 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 = 0; + 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 && @@ -6768,22 +8358,72 @@ namespace cimg_library { //! 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) { + 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 = 0; + 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(); + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) + wait_all(); } #if cimg_display==0 - //! Wait for a window event in any CImg window. + //! 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 + CImgDisplay& render(const CImg& 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 + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& 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 + const CImgDisplay& snapshot(CImg& img) const { + cimg::unused(img); + _no_display_exception(); + return *this; + } #endif // X11-based implementation @@ -6805,7 +8445,7 @@ namespace cimg_library { if (!dpy) { Display *const _dpy = XOpenDisplay(0); if (!_dpy) - throw CImgDisplayException("CImgDisplay::screen_width() : Failed to open X11 display."); + throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display."); res = DisplayWidth(_dpy,DefaultScreen(_dpy)); XCloseDisplay(_dpy); } else { @@ -6826,7 +8466,7 @@ namespace cimg_library { if (!dpy) { Display *const _dpy = XOpenDisplay(0); if (!_dpy) - throw CImgDisplayException("CImgDisplay::screen_height() : Failed to open X11 display."); + throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display."); res = DisplayHeight(_dpy,DefaultScreen(_dpy)); XCloseDisplay(_dpy); } else { @@ -6842,20 +8482,10 @@ namespace cimg_library { } static void wait_all() { - Display *const dpy = cimg::X11_attr().display; - if (!dpy) return; - XLockDisplay(dpy); - bool flag = true; - XEvent event; - while (flag) { - XNextEvent(dpy,&event); - for (unsigned int i = 0; i_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) { - cimg::X11_attr().wins[i]->_handle_events(&event); - if (cimg::X11_attr().wins[i]->_is_event) flag = false; - } - } - XUnlockDisplay(dpy); + 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) { @@ -6867,6 +8497,7 @@ namespace cimg_library { (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 : { @@ -6877,8 +8508,12 @@ namespace cimg_library { _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); } - if (nx!=_window_x || ny!=_window_y) { _window_x = nx; _window_y = ny; _is_moved = _is_event = true; } } break; case Expose : { while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {} @@ -6886,7 +8521,7 @@ namespace cimg_library { if (_is_fullscreen) { XWindowAttributes attr; XGetWindowAttributes(dpy,_window,&attr); - while (attr.map_state!=IsViewable) XSync(dpy,False); + while (attr.map_state!=IsViewable) XSync(dpy,0); XSetInputFocus(dpy,_window,RevertToParent,CurrentTime); } } break; @@ -6920,9 +8555,15 @@ namespace cimg_library { set_key((unsigned int)ksym,true); } break; case KeyRelease : { - char tmp = 0; KeySym ksym; - XLookupString(&event.xkey,&tmp,1,&ksym,0); - set_key((unsigned int)ksym,false); + 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)) {} @@ -6932,7 +8573,8 @@ namespace cimg_library { } break; case LeaveNotify : { while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {} - _mouse_x = _mouse_y =-1; _is_event = true; + _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)) {} @@ -6940,65 +8582,67 @@ namespace cimg_library { _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 *) { // Only one thread to handle events for all opened display windows. + 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); - for (;;) { - XLockDisplay(dpy); + 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); + ExposureMask | StructureNotifyMask | ButtonPressMask | + KeyPressMask | PointerMotionMask | EnterWindowMask | + LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event); if (event_flag) for (unsigned int i = 0; i_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) cimg::X11_attr().wins[i]->_handle_events(&event); - XUnlockDisplay(dpy); + cimg_unlock_display(); pthread_testcancel(); cimg::sleep(8); } return 0; } - void _set_colormap(Colormap& colormap, const unsigned int dim) { - XColor palette[256]; + void _set_colormap(Colormap& _colormap, const unsigned int dim) { + XColor *const colormap = new XColor[256]; switch (dim) { - case 1 : { // palette for greyscale images + case 1 : { // colormap for greyscale images for (unsigned int index = 0; index<256; ++index) { - palette[index].pixel = index; - palette[index].red = palette[index].green = palette[index].blue = (unsigned short)(index<<8); - palette[index].flags = DoRed | DoGreen | DoBlue; + 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 : { // palette for RG images + 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) { - palette[index].pixel = index; - palette[index].red = palette[index].blue = (unsigned short)(r<<8); - palette[index].green = (unsigned short)(g<<8); - palette[index++].flags = DoRed | DoGreen | DoBlue; + 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 : { // palette for RGB images + 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) { - palette[index].pixel = index; - palette[index].red = (unsigned short)(r<<8); - palette[index].green = (unsigned short)(g<<8); - palette[index].blue = (unsigned short)(b<<8); - palette[index++].flags = DoRed | DoGreen | DoBlue; + 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,palette,256); + XStoreColors(cimg::X11_attr().display,_colormap,colormap,256); + delete[] colormap; } void _map_window() { @@ -7016,7 +8660,7 @@ namespace cimg_library { } while (!is_exposed || !is_mapped); do { // Wait for the window to be visible. XGetWindowAttributes(dpy,_window,&attr); - if (attr.map_state!=IsViewable) { XSync(dpy,False); cimg::sleep(10); } + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } } while (attr.map_state!=IsViewable); _window_x = attr.x; _window_y = attr.y; @@ -7026,10 +8670,10 @@ namespace cimg_library { 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. - static XEvent event; + XEvent event; event.xexpose.type = Expose; event.xexpose.serial = 0; - event.xexpose.send_event = True; + event.xexpose.send_event = 1; event.xexpose.display = dpy; event.xexpose.window = _window; event.xexpose.x = 0; @@ -7037,16 +8681,12 @@ namespace cimg_library { event.xexpose.width = width(); event.xexpose.height = height(); event.xexpose.count = 0; - XSendEvent(dpy,_window,False,0,&event); + 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) { - const int completion_type = XShmGetEventBase(dpy) + ShmCompletion; - XEvent event; - XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,True); - do { XNextEvent(dpy,&event); } while (event.type!=completion_type); // Wait for the image drawing to be completed. - } else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); + 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 @@ -7069,9 +8709,10 @@ namespace cimg_library { 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 = False; + 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); @@ -7141,23 +8782,24 @@ namespace cimg_library { XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy), cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime); XRRFreeScreenConfigInfo(config); - XSync(dpy,False); + XSync(dpy,0); } } } if (!cimg::X11_attr().resolutions) cimg::warn(_cimgdisplay_instance - "init_fullscreen() : Xrandr extension not supported by the X server.", + "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 = True; + winattr.override_redirect = 1; _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0, InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); - const unsigned int buf_size = sx*sy*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + 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, @@ -7169,14 +8811,14 @@ namespace cimg_library { 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,False); + 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,False); + while (attr.map_state!=IsViewable) XSync(dpy,0); XDestroyImage(background_image); } @@ -7189,7 +8831,7 @@ namespace cimg_library { XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime); XRRFreeScreenConfigInfo(config); - XSync(dpy,False); + XSync(dpy,0); cimg::X11_attr().curr_resolution = 0; } #endif @@ -7207,10 +8849,11 @@ namespace cimg_library { 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 = std::strlen(nptitle) + 1; + 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)); @@ -7220,18 +8863,17 @@ namespace cimg_library { // Open X11 display and retrieve graphical properties. Display* &dpy = cimg::X11_attr().display; if (!dpy) { - static const int xinit_status = XInitThreads(); - cimg::unused(xinit_status); dpy = XOpenDisplay(0); if (!dpy) throw CImgDisplayException(_cimgdisplay_instance - "assign() : Failed to open X11 display.", + "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) + 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 " + "assign(): Invalid %u bits screen mode detected " "(only 8, 16, 24 and 32 bits modes are managed).", cimgdisplay_instance, cimg::X11_attr().nb_bits); @@ -7243,14 +8885,14 @@ namespace cimg_library { cimg::X11_attr().byte_order = ImageByteOrder(dpy); XFree(vinfo); - XLockDisplay(dpy); - cimg::X11_attr().event_thread = new pthread_t; - pthread_create(cimg::X11_attr().event_thread,0,_events_thread,0); - } else XLockDisplay(dpy); + 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 = cimg::min(dimw,(unsigned int)screen_width()); - _height = cimg::min(dimh,(unsigned int)screen_height()); + _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; @@ -7263,8 +8905,8 @@ namespace cimg_library { if (!_is_closed) _init_fullscreen(); const unsigned int sx = screen_width(), sy = screen_height(); XSetWindowAttributes winattr; - winattr.override_redirect = True; - _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx-_width)/2,(sy-_height)/2,_width,_height,0,0, + 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); @@ -7295,23 +8937,26 @@ namespace cimg_library { _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); + _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 = False; + 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,False); + 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; + shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); + delete _shminfo; _shminfo = 0; } } } @@ -7320,30 +8965,33 @@ namespace cimg_library { if (!_shminfo) #endif { - const unsigned int buf_size = _width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + 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); + _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",False); - _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",False); + _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,True,GrabModeAsync,GrabModeAsync,CurrentTime); + 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::min(); } - XUnlockDisplay(dpy); + cimg_unlock_display(); + cimg::mutex(14,0); } CImgDisplay& assign() { if (is_empty()) return flush(); Display *const dpy = cimg::X11_attr().display; - XLockDisplay(dpy); + cimg_lock_display(); // Remove display window from event thread list. unsigned int i; for (i = 0; i tmp; - const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + const CImg& 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(); @@ -7421,7 +9060,9 @@ namespace cimg_library { const bool fullscreen_flag=false, const bool closed_flag=false) { if (!list) return assign(); CImg tmp; - const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + const CImg 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(); @@ -7432,7 +9073,7 @@ namespace cimg_library { _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))*_width*_height); + sizeof(unsigned int))*(size_t)_width*_height); return paint(); } @@ -7441,21 +9082,32 @@ namespace cimg_library { 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), + tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100), + tmpdimy = (nheight>0)?nheight:(-nheight*height()/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; - XLockDisplay(dpy); - if (_window_width!=dimx || _window_height!=dimy) XResizeWindow(dpy,_window,dimx,dimy); - 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); } + 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); + } } - _window_width = _width = dimx; _window_height = _height = dimy; + 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; - XUnlockDisplay(dpy); - if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2); + if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2); if (force_redraw) return paint(); return *this; } @@ -7463,7 +9115,8 @@ namespace cimg_library { CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { if (is_empty()) return *this; if (force_redraw) { - const unsigned int buf_size = _width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + 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); @@ -7476,91 +9129,93 @@ namespace cimg_library { CImgDisplay& show() { if (is_empty() || !_is_closed) return *this; - Display *const dpy = cimg::X11_attr().display; - XLockDisplay(dpy); + cimg_lock_display(); if (_is_fullscreen) _init_fullscreen(); _map_window(); _is_closed = false; - XUnlockDisplay(dpy); + cimg_unlock_display(); return paint(); } CImgDisplay& close() { if (is_empty() || _is_closed) return *this; Display *const dpy = cimg::X11_attr().display; - XLockDisplay(dpy); + cimg_lock_display(); if (_is_fullscreen) _desinit_fullscreen(); XUnmapWindow(dpy,_window); _window_x = _window_y = -1; _is_closed = true; - XUnlockDisplay(dpy); + cimg_unlock_display(); return *this; } CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; - Display *const dpy = cimg::X11_attr().display; - show(); - XLockDisplay(dpy); - XMoveWindow(dpy,_window,posx,posy); - _window_x = posx; _window_y = posy; + 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; - XUnlockDisplay(dpy); return paint(); } CImgDisplay& show_mouse() { if (is_empty()) return *this; Display *const dpy = cimg::X11_attr().display; - XLockDisplay(dpy); + cimg_lock_display(); XUndefineCursor(dpy,_window); - XUnlockDisplay(dpy); + cimg_unlock_display(); return *this; } CImgDisplay& hide_mouse() { if (is_empty()) return *this; Display *const dpy = cimg::X11_attr().display; - XLockDisplay(dpy); - const char pix_data[8] = { 0 }; + 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); - XUnlockDisplay(dpy); + 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; - XLockDisplay(dpy); + cimg_lock_display(); XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy); _mouse_x = posx; _mouse_y = posy; _is_moved = false; - XSync(dpy,False); - XUnlockDisplay(dpy); + XSync(dpy,0); + cimg_unlock_display(); return *this; } CImgDisplay& set_title(const char *const format, ...) { if (is_empty()) return *this; - char tmp[1024] = { 0 }; + char *const tmp = new char[1024]; va_list ap; va_start(ap, format); - cimg_vsnprintf(tmp,sizeof(tmp),format,ap); + cimg_vsnprintf(tmp,1024,format,ap); va_end(ap); - if (!std::strcmp(_title,tmp)) return *this; + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } delete[] _title; - const unsigned int s = std::strlen(tmp) + 1; + 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; - XLockDisplay(dpy); + cimg_lock_display(); XStoreName(dpy,_window,tmp); - XUnlockDisplay(dpy); + cimg_unlock_display(); + delete[] tmp; return *this; } @@ -7568,7 +9223,7 @@ namespace cimg_library { CImgDisplay& display(const CImg& img) { if (!img) throw CImgArgumentException(_cimgdisplay_instance - "display() : Empty specified image.", + "display(): Empty specified image.", cimgdisplay_instance); if (is_empty()) return assign(img); return render(img).paint(false); @@ -7576,10 +9231,9 @@ namespace cimg_library { CImgDisplay& paint(const bool wait_expose=true) { if (is_empty()) return *this; - Display *const dpy = cimg::X11_attr().display; - XLockDisplay(dpy); + cimg_lock_display(); _paint(wait_expose); - XUnlockDisplay(dpy); + cimg_unlock_display(); return *this; } @@ -7587,159 +9241,198 @@ namespace cimg_library { CImgDisplay& render(const CImg& img, const bool flag8=false) { if (!img) throw CImgArgumentException(_cimgdisplay_instance - "render() : Empty specified image.", + "render(): Empty specified image.", cimgdisplay_instance); if (is_empty()) return *this; - if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/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 (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::ucharT> default_palette = CImg::ucharT>::default_LUT256(); - return render(img.get_index(default_palette,true,false)); + static const CImg::ucharT> default_colormap = CImg::ucharT>::default_LUT256(); + return render(img.get_index(default_colormap,1,false)); } - Display *const dpy = cimg::X11_attr().display; 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); - XLockDisplay(dpy); + cimg_lock_display(); if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { _min = _max = 0; switch (cimg::X11_attr().nb_bits) { - case 8 : { // 256 color palette, no normalization + 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[img._width*img._height]; - unsigned char *ptrd = (unsigned char*)ndata; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++); + case 1 : + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + (*ptrd++) = (unsigned char)*(data1++); break; - case 2 : for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++); + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++); + 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; } + 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[img._width*img._height]; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)*(data1++), G = val>>2; - *(ptrd++) = (val&M) | (G>>3); - *(ptrd++) = (G<<5) | (G>>1); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)*(data1++), G = val>>2; - *(ptrd++) = (G<<5) | (G>>1); - *(ptrd++) = (val&M) | (G>>3); - } + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); - *(ptrd++) = (G<<5); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - *(ptrd++) = (G<<5); - *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); - } + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); - *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)*(data2++)>>2; - *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); - *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); - } + 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; } - 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[img._width*img._height]; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); else - for (unsigned int xy = img._width*img._height; xy>0; --xy) + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - *(ptrd++) = 0; - *(ptrd++) = (unsigned char)*(data1++); - *(ptrd++) = 0; - *(ptrd++) = 0; - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - *(ptrd++) = 0; - *(ptrd++) = 0; - *(ptrd++) = (unsigned char)*(data1++); - *(ptrd++) = 0; - } + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - *(ptrd++) = 0; - *(ptrd++) = (unsigned char)*(data2++); - *(ptrd++) = (unsigned char)*(data1++); - *(ptrd++) = 0; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - *(ptrd++) = 0; - *(ptrd++) = (unsigned char)*(data1++); - *(ptrd++) = (unsigned char)*(data2++); - *(ptrd++) = (unsigned char)*(data3++); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - *(ptrd++) = (unsigned char)*(data3++); - *(ptrd++) = (unsigned char)*(data2++); - *(ptrd++) = (unsigned char)*(data1++); - *(ptrd++) = 0; - } + 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; } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } } } } else { @@ -7749,164 +9442,242 @@ namespace cimg_library { } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); const float delta = _max - _min, mm = 255/(delta?delta:1.0f); switch (cimg::X11_attr().nb_bits) { - case 8 : { // 256 color palette, with normalization + 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[img._width*img._height]; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char R = (unsigned char)((*(data1++)-_min)*mm); + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { + 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); + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); (*ptrd++) = (R&0xf0) | (G>>4); } break; default : - for (unsigned int xy = img._width*img._height; xy>0; --xy) { + 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); + 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; } + 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[img._width*img._height]; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2; - *(ptrd++) = (val&M) | (G>>3); - *(ptrd++) = (G<<5) | (val>>3); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2; - *(ptrd++) = (G<<5) | (val>>3); - *(ptrd++) = (val&M) | (G>>3); - } + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; - *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3); - *(ptrd++) = (G<<5); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; - *(ptrd++) = (G<<5); - *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3); - } + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; - *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3); - *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2; - *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3); - *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3); - } + 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; } - 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[img._width*img._height]; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) + 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)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8); else - for (unsigned int xy = img._width*img._height; xy>0; --xy) + 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); + ((unsigned char)((*(data2++) - _min)*mm)<<16) | + ((unsigned char)((*(data1++) - _min)*mm)<<8); break; default : if (cimg::X11_attr().byte_order==cimg::endianness()) - for (unsigned int xy = img._width*img._height; xy>0; --xy) + 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); + ((unsigned char)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8) | + (unsigned char)((*(data3++) - _min)*mm); else - for (unsigned int xy = img._width*img._height; xy>0; --xy) + 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); + ((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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); - (*ptrd++) = 0; - (*ptrd++) = val; - (*ptrd++) = val; - (*ptrd++) = val; - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); - (*ptrd++) = val; - (*ptrd++) = val; - (*ptrd++) = val; - (*ptrd++) = 0; - } + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - (*ptrd++) = 0; - (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm); - (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm); - (*ptrd++) = 0; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { - (*ptrd++) = 0; - (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm); - (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm); - (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm); - (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm); - (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm); - (*ptrd++) = 0; - } + 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; } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } } } } - XUnlockDisplay(dpy); + cimg_unlock_display(); return *this; } + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& 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=0 && _y0red_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 const CImgDisplay& snapshot(CImg& img) const { if (is_empty()) { img.assign(); return *this; } @@ -7919,38 +9690,46 @@ namespace cimg_library { if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); switch (cimg::X11_attr().nb_bits) { case 8 : { - for (unsigned int xy = img._width*img._height; xy>0; --xy) { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { const unsigned char val = *(ptrs++); - *(data1++) = val&0xe0; - *(data2++) = (val&0x1c)<<3; - *(data3++) = val<<6; + *(data1++) = (T)(val&0xe0); + *(data2++) = (T)((val&0x1c)<<3); + *(data3++) = (T)(val<<6); } } break; case 16 : { - if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val0 = *(ptrs++), val1 = *(ptrs++); - *(data1++) = val0&0xf8; - *(data2++) = (val0<<5) | ((val1&0xe0)>>5); - *(data3++) = val1<<3; - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned short val0 = *(ptrs++), val1 = *(ptrs++); - *(data1++) = val1&0xf8; - *(data2++) = (val1<<5) | ((val0&0xe0)>>5); - *(data3++) = val0<<3; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { + if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { ++ptrs; - *(data1++) = *(ptrs++); - *(data2++) = *(ptrs++); - *(data3++) = *(ptrs++); - } else for (unsigned int xy = img._width*img._height; xy>0; --xy) { - *(data3++) = *(ptrs++); - *(data2++) = *(ptrs++); - *(data1++) = *(ptrs++); - ++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; @@ -7960,25 +9739,21 @@ namespace cimg_library { //------------------------------- #elif cimg_display==2 + bool _is_mouse_tracked, _is_cursor_visible; + HANDLE _thread, _is_created, _mutex; + HWND _window, _background_window; CLIENTCREATESTRUCT _ccs; - BITMAPINFO _bmi; unsigned int *_data; DEVMODE _curr_mode; - HWND _window; - HWND _background_window; + BITMAPINFO _bmi; HDC _hdc; - HANDLE _thread; - HANDLE _is_created; - HANDLE _mutex; - bool _is_mouse_tracked; - bool _is_cursor_visible; static int screen_width() { DEVMODE mode; mode.dmSize = sizeof(DEVMODE); mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); - return mode.dmPelsWidth; + return (int)mode.dmPelsWidth; } static int screen_height() { @@ -7986,14 +9761,14 @@ namespace cimg_library { mode.dmSize = sizeof(DEVMODE); mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); - return mode.dmPelsHeight; + 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) { + static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) { #ifdef _WIN64 CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); #else @@ -8037,6 +9812,12 @@ namespace cimg_library { } break; case WM_PAINT : disp->paint(); + cimg::mutex(15); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); + cimg::mutex(15,0); + break; + case WM_ERASEBKGND : + // return 0; break; case WM_KEYDOWN : disp->set_key((unsigned int)wParam); @@ -8063,10 +9844,16 @@ namespace cimg_library { disp->_mouse_x = disp->_mouse_y = -1; disp->_is_event = true; SetEvent(cimg::Win32_attr().wait_event); + cimg::mutex(15); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); + cimg::mutex(15,0); } break; case WM_MOUSELEAVE : { disp->_mouse_x = disp->_mouse_y = -1; disp->_is_mouse_tracked = false; + cimg::mutex(15); + while (ShowCursor(TRUE)<0) {} + cimg::mutex(15,0); } break; case WM_LBUTTONDOWN : disp->set_button(1); @@ -8095,10 +9882,6 @@ namespace cimg_library { case 0x020A : // WM_MOUSEWHEEL: disp->set_wheel((int)((short)HIWORD(wParam))/120); SetEvent(cimg::Win32_attr().wait_event); - case WM_SETCURSOR : - if (disp->_is_cursor_visible) ShowCursor(TRUE); - else ShowCursor(FALSE); - break; } return DefWindowProc(window,msg,wParam,lParam); } @@ -8119,14 +9902,14 @@ namespace cimg_library { disp->_bmi.bmiHeader.biYPelsPerMeter = 1; disp->_bmi.bmiHeader.biClrUsed = 0; disp->_bmi.bmiHeader.biClrImportant = 0; - disp->_data = new unsigned int[disp->_width*disp->_height]; + 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 = disp->_width-1; rect.bottom = disp->_height-1; + 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 = (rect.right - rect.left + 1 - disp->_width)/2, - border2 = rect.bottom - rect.top + 1 - disp->_height - border1; + 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, @@ -8137,9 +9920,13 @@ namespace cimg_library { disp->_window_y = rect.top + border2; } else disp->_window_x = disp->_window_y = 0; } else { // Fullscreen window - const unsigned int sx = screen_width(), sy = screen_height(); + 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, + 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; } @@ -8164,11 +9951,11 @@ namespace cimg_library { if (_is_closed) _window_x = _window_y = -1; else { RECT rect; - rect.left = rect.top = 0; rect.right = _width-1; rect.bottom = _height-1; + 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 = (rect.right - rect.left + 1 - _width)/2, - border2 = rect.bottom - rect.top + 1 - _height - border1; + 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; @@ -8197,7 +9984,9 @@ namespace cimg_library { ChangeDisplaySettings(&mode,0); } else _curr_mode.dmSize = 0; - const unsigned int sx = screen_width(), sy = screen_height(); + 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); @@ -8220,7 +10009,7 @@ namespace cimg_library { // Allocate space for window title const char *const nptitle = ptitle?ptitle:""; - const unsigned int s = std::strlen(nptitle) + 1; + 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)); @@ -8228,8 +10017,8 @@ namespace cimg_library { if (!is_empty()) assign(); // Set display variables - _width = cimg::min(dimw,(unsigned int)screen_width()); - _height = cimg::min(dimh,(unsigned int)screen_height()); + _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; @@ -8244,10 +10033,9 @@ namespace cimg_library { void *const arg = (void*)(new void*[2]); ((void**)arg)[0] = (void*)this; ((void**)arg)[1] = (void*)_title; - unsigned long ThreadID = 0; _mutex = CreateMutex(0,FALSE,0); _is_created = CreateEvent(0,FALSE,FALSE,0); - _thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID); + _thread = CreateThread(0,0,_events_thread,arg,0,0); WaitForSingleObject(_is_created,INFINITE); return *this; } @@ -8287,7 +10075,9 @@ namespace cimg_library { const bool fullscreen_flag=false, const bool closed_flag=false) { if (!img) return assign(); CImg tmp; - const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + const CImg& 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); @@ -8299,7 +10089,9 @@ namespace cimg_library { const bool fullscreen_flag=false, const bool closed_flag=false) { if (!list) return assign(); CImg tmp; - const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + const CImg 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); @@ -8320,26 +10112,29 @@ namespace cimg_library { tmpdimy = (nheight>0)?nheight:(-nheight*_height/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; - if (_window_width!=dimx || _window_height!=dimy) { - RECT rect; rect.left = rect.top = 0; rect.right = dimx - 1; rect.bottom = 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 || _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(); } - 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 = dimx; - _bmi.bmiHeader.biHeight = -(int)dimy; - _width = dimx; - _height = dimy; - } - _window_width = dimx; _window_height = dimy; _is_resized = false; - if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2); + if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2); if (force_redraw) return paint(); return *this; } @@ -8347,12 +10142,14 @@ namespace cimg_library { CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { if (is_empty()) return *this; if (force_redraw) { - const unsigned int buf_size = _width*_height*4; + const cimg_ulong buf_size = (cimg_ulong)_width*_height*4; void *odata = std::malloc(buf_size); - std::memcpy(odata,_data,buf_size); - assign(_width,_height,_title,_normalization,!_is_fullscreen,false); - std::memcpy(_data,odata,buf_size); - std::free(odata); + 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); @@ -8378,36 +10175,38 @@ namespace cimg_library { CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; - if (!_is_fullscreen) { - RECT rect; rect.left = rect.top = 0; rect.right = _window_width-1; rect.bottom = _window_height-1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int border1 = (rect.right-rect.left+1-_width)/2, border2 = 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; + 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 show(); + return *this; } CImgDisplay& show_mouse() { if (is_empty()) return *this; _is_cursor_visible = true; - ShowCursor(TRUE); - SendMessage(_window,WM_SETCURSOR,0,0); return *this; } CImgDisplay& hide_mouse() { if (is_empty()) return *this; _is_cursor_visible = false; - ShowCursor(FALSE); - SendMessage(_window,WM_SETCURSOR,0,0); return *this; } CImgDisplay& set_mouse(const int posx, const int posy) { - if (_is_closed || posx<0 || posy<0) return *this; + 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; } @@ -8416,17 +10215,18 @@ namespace cimg_library { CImgDisplay& set_title(const char *const format, ...) { if (is_empty()) return *this; - char tmp[1024] = { 0 }; + char *const tmp = new char[1024]; va_list ap; va_start(ap, format); - cimg_vsnprintf(tmp,sizeof(tmp),format,ap); + cimg_vsnprintf(tmp,1024,format,ap); va_end(ap); - if (!std::strcmp(_title,tmp)) return *this; + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } delete[] _title; - const unsigned int s = std::strlen(tmp) + 1; + 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; } @@ -8434,7 +10234,7 @@ namespace cimg_library { CImgDisplay& display(const CImg& img) { if (!img) throw CImgArgumentException(_cimgdisplay_instance - "display() : Empty specified image.", + "display(): Empty specified image.", cimgdisplay_instance); if (is_empty()) return assign(img); return render(img).paint(); @@ -8452,11 +10252,12 @@ namespace cimg_library { CImgDisplay& render(const CImg& img) { if (!img) throw CImgArgumentException(_cimgdisplay_instance - "render() : Empty specified image.", + "render(): Empty specified image.", cimgdisplay_instance); if (is_empty()) return *this; - if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2)); + 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, @@ -8465,25 +10266,35 @@ namespace cimg_library { WaitForSingleObject(_mutex,INFINITE); unsigned int - *const ndata = (img._width==_width && img._height==_height)?_data:new unsigned int[img._width*img._height], + *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::string()==cimg::type::string())) { _min = _max = 0; switch (img._spectrum) { case 1 : { - for (unsigned int xy = img._width*img._height; xy>0; --xy) { + 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; + *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); } } break; case 2 : { - for (unsigned int xy = img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) - *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + 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 { @@ -8494,26 +10305,26 @@ namespace cimg_library { const float delta = _max - _min, mm = 255/(delta?delta:1.0f); switch (img._spectrum) { case 1 : { - for (unsigned int xy = img._width*img._height; xy>0; --xy) { - const unsigned char val = (unsigned char)((*(data1++)-_min)*mm); - *(ptrd++) = (val<<16) | (val<<8) | val; + 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 (unsigned int xy = img._width*img._height; xy>0; --xy) { + 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<<16) | (G<<8); + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); } } break; default : { - for (unsigned int xy = img._width*img._height; xy>0; --xy) { + 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<<16) | (G<<8) | B; + 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); } } } @@ -8523,6 +10334,66 @@ namespace cimg_library { return *this; } + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& 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=0 && _y0 const CImgDisplay& snapshot(CImg& img) const { if (is_empty()) { img.assign(); return *this; } @@ -8532,11 +10403,11 @@ namespace cimg_library { *data1 = img.data(0,0,0,0), *data2 = img.data(0,0,0,1), *data3 = img.data(0,0,0,2); - for (unsigned int xy = img._width*img._height; xy>0; --xy) { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { const unsigned int val = *(ptrs++); - *(data1++) = (unsigned char)(val>>16); - *(data2++) = (unsigned char)((val>>8)&0xFF); - *(data3++) = (unsigned char)(val&0xFF); + *(data1++) = (T)(unsigned char)(val>>16); + *(data2++) = (T)(unsigned char)((val>>8)&0xFF); + *(data3++) = (T)(unsigned char)(val&0xFF); } return *this; } @@ -8565,18 +10436,19 @@ namespace cimg_library { \par Image representation A %CImg image is defined as an instance of the container \c CImg, 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 + 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 (x,y,z), while the number of channels - is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance). + Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), + 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 rather than simple images \c CImg. Thus, the \c CImg 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 : unsigned char, char, short, unsigned int, int, + Concerning the pixel value type \c T: + fully supported template types are the basic C++ types: unsigned char, char, short, unsigned int, int, unsigned long, long, float, double, ... . Typically, fast image display can be done using CImg images, while complex image processing algorithms may be rather coded using CImg or CImg @@ -8586,7 +10458,7 @@ namespace cimg_library { \par Image structure - The \c CImg structure contains \e six fields : + The \c CImg 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). @@ -8607,9 +10479,9 @@ namespace cimg_library { \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 : + Here is a list of the most used: - - Construct images from arbitrary dimensions : + - Construct images from arbitrary dimensions: - CImg img; declares an empty image. - CImg img(128,128); declares a 128x128 greyscale image with \c unsigned \c char pixel values. @@ -8620,33 +10492,32 @@ namespace cimg_library { (with \c double pixel values). - CImg<> img(128,128,128,3); 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 not automatically initialized to 0. You may use the function \c fill() to - do it, or use the specific constructor taking 5 parameters like this : + - \b Note: images pixels are not automatically initialized to 0. You may use the function \c fill() to + do it, or use the specific constructor taking 5 parameters like this: CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. - - Construct images from filenames : + - Construct images from filenames: - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". - - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr". - - \b Note : You need to install ImageMagick + - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the + file "analyze.hdr". + - \b Note: You need to install ImageMagick to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). - - Construct images from C-style arrays : + - Construct images from C-style arrays: - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer \c data_buffer (of size 256x256=65536). - - CImg img(data_buffer,256,256,1,3,false); constructs a 256x256 color image + - CImg img(data_buffer,256,256,1,3); constructs a 256x256 color image from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). - - CImg img(data_buffer,256,256,1,3,true); constructs a 256x256 color image - from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed). The complete list of constructors can be found here. \par Most useful functions The \c CImg class contains a lot of functions that operates on images. - Some of the most useful are : + Some of the most useful are: - - operator()() : allows to access or write pixel values. - - display() : displays the image in a new window. + - operator()(): Read or write pixel values. + - display(): displays the image in a new window. **/ template struct CImg { @@ -8661,13 +10532,13 @@ namespace cimg_library { - The \c CImg::iterator type is defined to be a 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. - \par Sample code : + \par Example \code CImg img("reference.jpg"); // Load image from file. - for (CImg::iterator it = img.begin(), it::iterator it = img.begin(), it::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. - \par Sample code : + \par Example \code const CImg img("reference.jpg"); // Load image from file. float sum = 0; - for (CImg::iterator it = img.begin(), it::iterator it = img.begin(), it::type Tshort; typedef typename cimg::superset::type Tuint; typedef typename cimg::superset::type Tint; - typedef typename cimg::superset::type Tulong; - typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; typedef typename cimg::superset::type Tfloat; typedef typename cimg::superset::type Tdouble; typedef typename cimg::last::type boolT; @@ -8717,8 +10588,10 @@ namespace cimg_library { typedef typename cimg::last::type shortT; typedef typename cimg::last::type uintT; typedef typename cimg::last::type intT; - typedef typename cimg::last::type ulongT; - typedef typename cimg::last::type longT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; typedef typename cimg::last::type floatT; typedef typename cimg::last::type doubleT; @@ -8763,111 +10636,107 @@ namespace cimg_library { //@{ //--------------------------------------------------------- - //! Destructor. + //! Destroy image. /** - Destroy current image instance. \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). - \sa CImg(), - assign(). + 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; } - //! Default constructor. + //! Construct empty image. /** - Construct a new empty image instance. \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, + - 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&). In all cases, the type of pixels stays \c T. - An empty image is never shared. - \par Sample code : + \par Example \code CImg 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 - \sa ~CImg(), - assign(), - is_empty(). **/ CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {} //! Construct image with specified size. /** - 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. - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). + \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). + - 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 Sample code : + \par Example \code CImg img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values. CImg img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'. \endcode - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int,T), - assign(unsigned int,unsigned int,unsigned int,unsigned int). **/ - 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): + 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) { - const unsigned int siz = size_x*size_y*size_z*size_c; + 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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c); + 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. /** - 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 set all pixel - values to specified \c value. - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param value : Value used for initialization. + \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). + - 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. - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int), - assign(unsigned int,unsigned int,unsigned int,unsigned int,T). **/ - CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value): + 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 unsigned int siz = size_x*size_y*size_z*size_c; + 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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c); + 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; } @@ -8875,14 +10744,15 @@ namespace cimg_library { //! 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 + 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 : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \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 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 @@ -8890,7 +10760,7 @@ namespace cimg_library { \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 Sample code : + \par Example \code const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image. 0,255,0,255, // Set the 4 values for the red component. @@ -8899,14 +10769,12 @@ namespace cimg_library { img.resize(150,150).display(); \endcode \image html ref_constructor1.jpg - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int), - CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...), - assign(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...). **/ 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) { + 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) { \ - unsigned int _siz = (unsigned int)N; \ + size_t _siz = (size_t)N; \ if (_siz--) { \ va_list ap; \ va_start(ap,a1); \ @@ -8914,25 +10782,125 @@ namespace cimg_library { *(ptrd++) = (T)a0; \ if (_siz--) { \ *(ptrd++) = (T)a1; \ - for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ + 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_x*size_y*size_z*size_c,int); + _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 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 + 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 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 + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, + std::initializer_list 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 + CImg(const unsigned int size_x, const unsigned int size_y, + std::initializer_list 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 + CImg(const unsigned int size_x, + std::initializer_list 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 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 + CImg(const std::initializer_list 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 + CImg & operator=(std::initializer_list 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 : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \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). + 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 @@ -8940,61 +10908,60 @@ namespace cimg_library { \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 : + For instance, the code below will probably crash on most platforms: \code - const CImg img(2,2,1,1, 0.5,0.5,255,255); // FAIL : The two last arguments are 'int', not 'double' ! + const CImg img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'! \endcode - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int), - CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), - assign(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...). **/ 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) { + 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_x*size_y*size_z*size_c,double); + _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 : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param values : Value string describing the way pixel values are set. - \param repeat_values : Flag telling if the value filling process is periodic. + 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 : + - 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 "1,2,3,7,8,2". 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 "cos(x/10)*sin(y/20)". 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 + - Either, \c values is a formula, as in "cos(x/10)*sin(y/20)". + 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 const char*. - A \c CImgArgumentException is thrown when an invalid value string \c values is specified. - \par Sample code : + \par Example \code - const CImg img1(129,129,1,3,"0,64,128,192,255",true), // Construct image filled from a value sequence. - img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image filled from a formula. + const CImg 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 - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int), - assign(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool). **/ 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 unsigned int siz = size_x*size_y*size_z*size_c; + 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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c); + 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; } @@ -9002,33 +10969,33 @@ namespace cimg_library { //! 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 : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param is_shared : Flag telling if input memory buffer must be shared by the current instance. + 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). + - 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 Sample code : + - 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 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 - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int), - assign(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool), - is_shared(). **/ template CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1, @@ -9036,30 +11003,31 @@ namespace cimg_library { 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(): 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::pixel_type()); } - const unsigned int siz = size_x*size_y*size_z*size_c; + 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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c); + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); } - const t *ptrs = values + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + 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 unsigned int siz = size_x*size_y*size_z*size_c; + 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(values); @@ -9067,86 +11035,91 @@ namespace cimg_library { 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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c); + 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)); } + std::memcpy(_data,values,siz*sizeof(T)); + } } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } } - //! Construct image from an image file. + //! 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 : Input image filename. + 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 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 Sample code : + - 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 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 img("reference.jpg"); img.display(); \endcode \image html ref_image.jpg - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int), - assign(const char*). **/ explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { assign(filename); } - //! Copy constructor. + //! Construct image copy. /** Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance. - \param img : Input image to copy. + \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. + - 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). - \sa CImg(const CImg&,bool), - assign(const CImg&), + - 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 CImg(const CImg& img):_is_shared(false) { - const unsigned int siz = img.size(); + 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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)), + 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 + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); } else { _width = _height = _depth = _spectrum = 0; _data = 0; } } - //! Copy constructor \specialization. + //! Construct image copy \specialization. CImg(const CImg& img) { - const unsigned int siz = img.size(); + 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; + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = img._is_shared; if (_is_shared) _data = const_cast(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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)), + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), img._width,img._height,img._depth,img._spectrum); } @@ -9159,57 +11132,56 @@ namespace cimg_library { /** Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance, while forcing the shared state of the constructed copy. - \param img : Input image to copy. - \param is_shared : Desired 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&), 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 : + 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. + - 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. - \sa CImg(const CImg&), - assign(const CImg&,bool). **/ template CImg(const CImg& 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(): Invalid construction request of a shared instance from a " "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).", cimg_instance, CImg::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data); } - const unsigned int siz = img.size(); + 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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)), + 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 + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + 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& img, const bool is_shared) { - const unsigned int siz = img.size(); + 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; + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = is_shared; if (_is_shared) _data = const_cast(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(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(img._width*img._height*img._depth*img._spectrum*sizeof(T)), + 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)); @@ -9219,45 +11191,42 @@ namespace cimg_library { //! 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 instance. - \param img : Input image from which dimensions are borrowed. - \param dimensions : String describing the image size along the X,Y,Z and C-dimensions. + Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing + \c CImg 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 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&,const char*,T) instead. - \par Sample code : + In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg&,const char*,T) + instead. + \par Example \code const CImg 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 - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int), - CImg(const CImg&,const char*,T), - assign(const CImg&,const char*). **/ template - CImg(const CImg& img, const char *const dimensions):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + CImg(const CImg& 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 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. + Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing + \c CImg 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&,const char*), but it also fills the pixel buffer with the specified \c value. - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int,T), - CImg(const CImg&,const char*), - assign(const CImg&,const char*,T). **/ template - CImg(const CImg& img, const char *const dimensions, const T value): + CImg(const CImg& 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); } @@ -9265,28 +11234,32 @@ namespace cimg_library { //! 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. + \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 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. - \sa CImgDisplay, - assign(const CImgDisplay&). **/ explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { disp.snapshot(*this); } - //! In-place version of the default constructor/destructor. + // 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&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + swap(img); + } + CImg& operator=(CImg&& 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. - \note - - It reinitializes the current image instance to a new constructed image instance. - - Memory used by the previous pixel buffer of the image instance is deallocated if necessary (i.e. if instance was not empty nor shared). - - If the image instance was shared, it is replaced by a (non-shared) empty image without a deallocation process. - - It can be useful to force memory deallocation of a pixel buffer used by an image instance, before its formal destruction. - \sa CImg(), - ~CImg(). **/ CImg& assign() { if (!_is_shared) delete[] _data; @@ -9294,26 +11267,20 @@ namespace cimg_library { return *this; } - //! In-place version of a constructor. + //! Construct image with specified size \inplace. /** In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int). - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(unsigned int,unsigned int,unsigned int,unsigned int,T). - CImg(unsigned int,unsigned int,unsigned int,unsigned int). **/ - CImg& 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 unsigned int siz = size_x*size_y*size_z*size_c; + CImg& 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 unsigned int curr_siz = size(); + 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).", + "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 { @@ -9321,9 +11288,10 @@ namespace cimg_library { 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).", + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c); + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); } } } @@ -9331,116 +11299,71 @@ namespace cimg_library { return *this; } - //! In-place version of a constructor. + //! 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). - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param value : Value for initialization. - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(unsigned int,unsigned int,unsigned int,unsigned int), - CImg(unsigned int,unsigned int,unsigned int,unsigned int,T). **/ - CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value) { + CImg& 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); } - //! In-place version of a constructor. + //! 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,...). - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param value0 : First value of the initialization sequence (must be an integer). - \param value1 : Second value of the initialization sequence (must be an integer). - \param ... - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...), - CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...). **/ - CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + CImg& 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_x*size_y*size_z*size_c,int); + _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int); return *this; } - //! In-place version of a constructor. + //! 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,...). - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param value0 : First value of the initialization sequence (must be a double). - \param value1 : Second value of the initialization sequence (must be a double). - \param ... - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), - CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...). **/ - CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + CImg& 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_x*size_y*size_z*size_c,double); + _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double); return *this; } - //! In-place version of a constructor. + //! 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). - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param values : Value string describing the way pixel values are set. - \param repeat_values : Flag telling if filling process is periodic. - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool). **/ - CImg& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + CImg& 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); } - //! In-place version of a constructor. + //! 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,bool). - \param values : Pointer to the input memory buffer. - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool). - CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool). + In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int). **/ template CImg& 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 unsigned int siz = size_x*size_y*size_z*size_c; + 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 + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); return *this; } - //! In-place version of a constructor \specialization. + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. CImg& 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 unsigned int siz = size_x*size_y*size_z*size_c; + const size_t siz = (size_t)size_x*size_y*size_z*size_c; if (!values || !siz) return assign(); - const unsigned int curr_siz = size(); + 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()) { + if (_is_shared || values + siz<_data || values>=_data + size()) { assign(size_x,size_y,size_z,size_c); if (_is_shared) std::memmove(_data,values,siz*sizeof(T)); else std::memcpy(_data,values,siz*sizeof(T)); @@ -9449,9 +11372,10 @@ namespace cimg_library { 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).", + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", cimg_instance, - cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c); + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); } std::memcpy(new_data,values,siz*sizeof(T)); delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; @@ -9459,49 +11383,30 @@ namespace cimg_library { return *this; } - //! In-place version of a constructor. - /** - In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool). - \param values : Pointer to the input memory buffer. - \param size_x : Desired image width(). - \param size_y : Desired image height(). - \param size_z : Desired image depth(). - \param size_c : Desired image spectrum(). - \param is_shared : Flag telling if input memory buffer must be shared by the current instance. - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(const t*,unsigned int,unsigned int,unsigned int,unsigned int). - CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int,bool). - **/ + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. template CImg& 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" + "assign(): Invalid assignment request of shared instance from (%s*) buffer" "(pixel types are different).", cimg_instance, CImg::pixel_type()); return assign(values,size_x,size_y,size_z,size_c); } - //! In-place version of a constructor \specialization. + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. CImg& 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 unsigned int siz = size_x*size_y*size_z*size_c; - if (!values || !siz) { - if (is_shared) - throw CImgArgumentException(_cimg_instance - "assign() : Invalid assignment request of shared instance from (null) or empty buffer.", - cimg_instance); - else return assign(); - } + 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(); + if (values + siz<_data || values>=_data + size()) assign(); else cimg::warn(_cimg_instance - "assign() : Shared image instance has overlapping memory.", + "assign(): Shared image instance has overlapping memory.", cimg_instance); } _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; @@ -9510,28 +11415,17 @@ namespace cimg_library { return *this; } - //! In-place version of a constructor. + //! Construct image from reading an image file \inplace. /** In-place version of the constructor CImg(const char*). - \param filename : Input image filename. - \note - - It reinitializes the current image instance to a new constructed image instance. - - Equivalent to load(const char*). - \sa CImg(const char*), - load(const char*). **/ CImg& assign(const char *const filename) { return load(filename); } - //! In-place version of the default copy constructor. + //! Construct image copy \inplace. /** In-place version of the constructor CImg(const CImg&). - \param img : Input image to copy. - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(const CImg&,bool), - CImg(const CImg&). **/ template CImg& assign(const CImg& img) { @@ -9541,47 +11435,38 @@ namespace cimg_library { //! In-place version of the advanced copy constructor. /** In-place version of the constructor CImg(const CImg&,bool). - \param img : Input image to copy. - \param is_shared : Desired shared state of the constructed copy. - \sa assign(const CImg&), - CImg(const CImg&,bool). **/ template CImg& assign(const CImg& img, const bool is_shared) { return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared); } - //! In-place version of a constructor. + //! Construct image with dimensions borrowed from another image \inplace. /** In-place version of the constructor CImg(const CImg&,const char*). - \param img : Input image from which dimensions are borrowed. - \param dimensions : String describing the image size along the X,Y,Z and V-dimensions. - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(const CImg&,const char*,T), - CImg(const CImg&,const char*). - **/ + **/ template CImg& assign(const CImg& 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 item(256); for (const char *s = dimensions; *s && k<4; ++k) { - char item[256] = { 0 }; - if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item)>0) s+=std::strlen(item); + 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 (std::sscanf(s,"%u%c",&val,&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::uncase(*s)) { + 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'.", + "assign(): Invalid character '%c' detected in specified dimension string '%s'.", cimg_instance, *s,dimensions); } @@ -9590,41 +11475,29 @@ namespace cimg_library { return assign(siz[0],siz[1],siz[2],siz[3]); } - //! In-place version of a constructor. + //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace. /** In-place version of the constructor CImg(const CImg&,const char*,T). - \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 for initialization. - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa assign(const CImg&,const char*), - CImg(const CImg&,const char*,T). - **/ + **/ template - CImg& assign(const CImg& img, const char *const dimensions, const T value) { + CImg& assign(const CImg& img, const char *const dimensions, const T& value) { return assign(img,dimensions).fill(value); } - //! In-place version of a constructor. + //! Construct image from a display window \inplace. /** In-place version of the constructor CImg(const CImgDisplay&). - \param disp : Input \c CImgDisplay. - \note - - It reinitializes the current image instance to a new constructed image instance. - \sa CImg(const CImgDisplay&). **/ CImg& assign(const CImgDisplay &disp) { disp.snapshot(*this); return *this; } - //! In-place version of the default constructor. + //! Construct empty image \inplace. /** Equivalent to assign(). \note - It has been defined for compatibility with STL naming conventions. - \sa assign(). **/ CImg& clear() { return assign(); @@ -9635,18 +11508,16 @@ namespace cimg_library { 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. + \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 Sample code : + - 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 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 - \sa move_to(CImgList&,unsigned int), - swap(CImg&). **/ template CImg& move_to(CImg& img) { @@ -9667,21 +11538,19 @@ namespace cimg_library { /** 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 instance. - \param list : Destination list. - \param pos : Position of the newly inserted image in the list. + \param list Destination list. + \param pos Position of the newly inserted image in the list. \note - - When optionnal parameter \c pos is ommited, the image instance is transfered as a new + - 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 Sample code : + \par Example \code CImgList list; // Construct an empty image list. CImg 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 - \sa move_to(CImg&), - swap(CImg&). **/ template CImgList& move_to(CImgList& list, const unsigned int pos=~0U) { @@ -9692,11 +11561,11 @@ namespace cimg_library { //! Swap fields of two image instances. /** - \param img : Image to swap fields with. + \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 Sample code : + \par Example \code CImg img1("lena.jpg"), img2("milla.jpg"); @@ -9704,19 +11573,17 @@ namespace cimg_library { \endcode **/ CImg& swap(CImg& img) { - cimg::swap(_width,img._width); - cimg::swap(_height,img._height); - cimg::swap(_depth,img._depth); - cimg::swap(_spectrum,img._spectrum); + 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; } - //! Get a reference to an empty image. + //! Return a reference to an empty image. /** \note - This function is useful mainly to declare optional parameters having type \c CImg in functions prototypes, e.g. + This function is useful mainly to declare optional parameters having type \c CImg in functions prototypes, + e.g. \code void f(const int x=0, const int y=0, const CImg& img=CImg::empty()); \endcode @@ -9726,6 +11593,12 @@ namespace cimg_library { return _empty.assign(); } + //! Return a reference to an empty image \const. + static const CImg& const_empty() { + static const CImg _empty; + return _empty; + } + //@} //------------------------------------------ // @@ -9738,77 +11611,76 @@ namespace cimg_library { 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 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. + \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 (0,0,0,0) to (width()-1,height()-1,depth()-1,spectrum()-1). - - 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 img(x,y,c) instead of img(x,y,0,c). + - Range of pixel coordinates start from (0,0,0,0) to + (width() - 1,height() - 1,depth() - 1,spectrum() - 1). + - 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 img(x,y,c) instead of + img(x,y,0,c). \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 Sample code : + 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 img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'. + CImg 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. + 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 - \sa at(), - atX(), - atXY(), - atXYZ(), - atXYZC(). **/ #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 unsigned int off = (unsigned int)offset(x,y,z,c); + 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 (%u,%u,%u,%u) [offset=%u].", + "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].", cimg_instance, - x,y,z,c,off); + (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 { + 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*>(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 width()*\ref height(). - \param whd : Precomputed offset, must be equal to width()*\ref height()*\ref depth(). + \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 width()*\ref height(). + \param whd Precomputed offset, must be equal to width()*\ref height()*\ref depth(). \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). - \sa operator()(). **/ T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, - const unsigned long wh, const unsigned long whd=0) { + 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 unsigned long wh, const unsigned long whd=0) const { + const ulongT wh, const ulongT whd=0) const { cimg::unused(wh,whd); return (*this)(x,y,z,c); } @@ -9830,38 +11702,38 @@ namespace cimg_library { } T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) { - return _data[x + y*_width + z*_width*_height]; + 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*_width + z*_width*_height]; + 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*_width + z*_width*_height + c*_width*_height*_depth]; + 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*_width + z*_width*_height + c*_width*_height*_depth]; + 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 unsigned long wh) { + 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 unsigned long wh) const { + 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 unsigned long wh, const unsigned long whd) { + 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 unsigned long wh, const unsigned long whd) const { + const ulongT wh, const ulongT whd) const { return _data[x + y*_width + z*wh + c*whd]; } #endif @@ -9884,10 +11756,9 @@ namespace cimg_library { - It also allows to use brackets to access pixel values, without need for a \c CImg::operator[](), e.g. \code CImg img(100,100); - const float value = img[99]; // Access to value of the last pixel on the first line. + 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 - \sa operator()(). **/ operator T*() { return _data; @@ -9901,54 +11772,51 @@ namespace cimg_library { //! 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. + \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 Sample code + \par Example \code CImg 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 - \sa fill(const T). **/ - CImg& operator=(const T value) { + CImg& 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. + \param expression Value string describing the way pixel values are set. \note - - String parameter \c expression may describe different things : + - 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 Sample code : + - 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 img1(100,100), img2(img1), img3(img1); // Declare three 100x100 scalar images with unitialized pixel 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). + CImg 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 - \sa fill(const char*, bool), - load(const char*). **/ CImg& operator=(const char *const expression) { const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; + cimg::exception_mode(0); try { - fill(expression,true); + _fill(expression,true,true,0,0,"operator=",0); } catch (CImgException&) { - cimg::exception_mode() = omode; + cimg::exception_mode(omode); load(expression); } - cimg::exception_mode() = omode; + cimg::exception_mode(omode); return *this; } @@ -9978,119 +11846,80 @@ namespace cimg_library { //! In-place addition operator. /** Add specified \c value to all pixels of an image instance. - \param value : Value to add. + \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 is possible but does nothing indeed. + - Resulting pixel values are casted to fit the pixel type \c T. + For instance, adding \c 0.2 to a \c CImg is possible but does nothing indeed. - Overflow values are treated as with standard C++ numeric types. For instance, \code CImg 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 Sample code : + - 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 img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]). - CImg 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'. + CImg img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]). + CImg 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 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 - \sa operator+(const t) const, - operator-=(const t), - operator*=(const t), - operator/=(const t), - operator%=(const t), - operator&=(const t), - operator|=(const t), - operator^=(const t), - operator<<=(const t), - operator>>=(const t). **/ template CImg& operator+=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd + value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288)) + cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd + value); 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. + \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. - \sa operator+=(const t), - operator=(const char*), - operator+(const char*) const, - operator-=(const char*), - operator*=(const char*), - operator/=(const char*), - operator%=(const char*), - operator&=(const char*), - operator|=(const char*), - operator^=(const char*), - operator<<=(const char*), - operator>>=(const char*). **/ CImg& operator+=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator+="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this+=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this+=(+*this)._fill(expression,true,true,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. + \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 cyclically. 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 Sample code : + - 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 img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3) - const CImg img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); // Construct a scalar shading (img2.spectrum()==1). - img1+=img2; // Add shading to each channel of 'img1'. - img1.cut(0,255); // Prevent [0,255] overflow. + CImg img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3) + // Construct a scalar shading (img2.spectrum()==1). + const CImg 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 - \sa operator+(const CImg&) const, - operator=(const CImg&), - operator-=(const CImg&), - operator*=(const CImg&), - operator/=(const CImg&), - operator%=(const CImg&), - operator&=(const CImg&), - operator|=(const CImg&), - operator^=(const CImg&), - operator<<=(const CImg&), - operator>>=(const CImg&). **/ template CImg& operator+=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator++() { - cimg_for(*this,ptrd,T) ++*ptrd; + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288)) + cimg_rof(*this,ptrd,T) ++*ptrd; return *this; } @@ -10113,10 +11942,8 @@ namespace cimg_library { /** 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. - \sa operator++(), - operator--(int). + - 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 operator++(int) { const CImg copy(*this,false); @@ -10124,18 +11951,14 @@ namespace cimg_library { return copy; } - //! Get a non-shared copy of the image instance. + //! 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(const CImg&) 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 informations about the shared state - of the input image. + Indeed, the usual copy constructor CImg(const CImg&) 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(img,false). - \sa CImg(const CImg&), - CImg(const CImg&,bool), - operator-() const, - operator~() const. **/ CImg operator+() const { return CImg(*this,false); @@ -10176,7 +11999,9 @@ namespace cimg_library { **/ template CImg& operator-=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd - value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288)) + cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd - value); return *this; } @@ -10185,19 +12010,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a substraction instead of an addition. **/ CImg& operator-=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator-="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this-=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this-=(+*this)._fill(expression,true,true,0,0,"operator-=",this); } //! In-place substraction operator. @@ -10206,12 +12019,13 @@ namespace cimg_library { **/ template CImg& operator-=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator--() { - cimg_for(*this,ptrd,T) *ptrd = *ptrd-(T)1; + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288)) + cimg_rof(*this,ptrd,T) *ptrd = *ptrd - (T)1; return *this; } @@ -10239,9 +12055,9 @@ namespace cimg_library { //! 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 Sample code : + - 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 img1("reference.jpg"), // Load a RGB color image. @@ -10249,8 +12065,6 @@ namespace cimg_library { (img1,img2).display(); \endcode \image html ref_operator_minus.jpg - \sa operator+(), - operator~(). **/ CImg operator-() const { return CImg(_width,_height,_depth,_spectrum,(T)0)-=*this; @@ -10291,7 +12105,9 @@ namespace cimg_library { **/ template CImg& operator*=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd * value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144)) + cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd * value); return *this; } @@ -10300,37 +12116,25 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. **/ CImg& operator*=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator*="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - mul(CImg(_width,_height,_depth,_spectrum,expression,true)); - } - cimg::exception_mode() = omode; - return *this; + return mul((+*this)._fill(expression,true,true,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. + 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&) instead. + - It does \e not compute a pointwise multiplication between two images. For this purpose, use + mul(const CImg&) instead. - The size of the image instance can be modified by this operator. - \par Sample code : + \par Example \code CImg A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4]. const CImg 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 - \sa operator*(const CImg&) const, - mul(). **/ template CImg& operator*=(const CImg& img) { @@ -10365,16 +12169,22 @@ namespace cimg_library { CImg<_cimg_Tt> operator*(const CImg& img) const { 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)", + "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<_cimg_Tt> res(img._width,_height); - _cimg_Tt value, *ptrd = res._data; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=1000 && img.size()>=1000) private(value) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(size()>1024 && img.size()>1024)) + cimg_forXY(res,i,j) { + _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value; + } +#else + _cimg_Tt *ptrd = res._data; + cimg_forXY(res,i,j) { + _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value; + } #endif - cimg_forXY(res,i,j) { value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = value; } return res; } @@ -10384,7 +12194,9 @@ namespace cimg_library { **/ template CImg& operator/=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd / value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd / value); return *this; } @@ -10393,32 +12205,19 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a division instead of an addition. **/ CImg& operator/=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator/="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - div(CImg(_width,_height,_depth,_spectrum,expression,true)); - } - cimg::exception_mode() = omode; - return *this; + return div((+*this)._fill(expression,true,true,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. + 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&) instead. + - It does \e not compute a pointwise division between two images. For this purpose, use + div(const CImg&) instead. - It returns the matrix operation \c A*inverse(img). - The size of the image instance can be modified by this operator. - \sa operator/(const CImg&) const, - operator*(const CImg&) const, - div(). **/ template CImg& operator/=(const CImg& img) { @@ -10460,7 +12259,9 @@ namespace cimg_library { **/ template CImg& operator%=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=16384)) + cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value); return *this; } @@ -10469,19 +12270,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. **/ CImg& operator%=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator%="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this%=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this%=(+*this)._fill(expression,true,true,0,0,"operator%=",this); } //! In-place modulo operator. @@ -10490,12 +12279,13 @@ namespace cimg_library { **/ template CImg& operator%=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg& operator&=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd & (ulongT)value); return *this; } @@ -10545,19 +12337,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. **/ CImg& operator&=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator&="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this&=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this&=(+*this)._fill(expression,true,true,0,0,"operator&=",this); } //! In-place bitwise AND operator. @@ -10566,13 +12346,14 @@ namespace cimg_library { **/ template CImg& operator&=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg& operator|=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd | (ulongT)value); return *this; } @@ -10621,19 +12404,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. **/ CImg& operator|=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator|="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this|=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this|=(+*this)._fill(expression,true,true,0,0,"operator|=",this); } //! In-place bitwise OR operator. @@ -10642,13 +12413,14 @@ namespace cimg_library { **/ template CImg& operator|=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg& operator^=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)value); return *this; } @@ -10701,19 +12475,7 @@ namespace cimg_library { - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. **/ CImg& operator^=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator^="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this^=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this^=(+*this)._fill(expression,true,true,0,0,"operator^=",this); } //! In-place bitwise XOR operator. @@ -10724,13 +12486,14 @@ namespace cimg_library { **/ template CImg& operator^=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg& operator<<=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536)) + cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) << (int)value); return *this; } @@ -10779,19 +12544,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. **/ CImg& operator<<=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator<<="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this<<=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this<<=(+*this)._fill(expression,true,true,0,0,"operator<<=",this); } //! In-place bitwise left shift operator. @@ -10800,13 +12553,14 @@ namespace cimg_library { **/ template CImg& operator<<=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs&), except that it returns a new image instance instead of operating in-place. + Similar to operator<<=(const CImg&), except that it returns a new image instance instead of + operating in-place. The pixel type of the returned image is \c T. **/ template @@ -10846,7 +12601,9 @@ namespace cimg_library { **/ template CImg& operator>>=(const t value) { - cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536)) + cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) >> (int)value); return *this; } @@ -10855,19 +12612,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. **/ CImg& operator>>=(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"operator<<="); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - cimg::exception_mode() = omode; - *this>>=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode() = omode; - return *this; + return *this>>=(+*this)._fill(expression,true,true,0,0,"operator>>=",this); } //! In-place bitwise right shift operator. @@ -10876,13 +12621,14 @@ namespace cimg_library { **/ template CImg& operator>>=(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs> (int)*(ptrs++)); - for (const t *ptrs = img._data; ptrd> (int)*(ptrs++)); + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs> (int)*(ptrs++)); + for (const t *ptrs = img._data; ptrd> (int)*(ptrs++)); } return *this; } @@ -10908,7 +12654,8 @@ namespace cimg_library { //! Bitwise right shift operator. /** - Similar to operator>>=(const CImg&), except that it returns a new image instance instead of operating in-place. + Similar to operator>>=(const CImg&), except that it returns a new image instance instead of + operating in-place. The pixel type of the returned image is \c T. **/ template @@ -10922,19 +12669,45 @@ namespace cimg_library { **/ CImg operator~() const { CImg res(_width,_height,_depth,_spectrum); - const T *ptrs = end(); - cimg_for(res,ptrd,T) { const unsigned long value = (unsigned long)*(--ptrs); *ptrd = (T)~value; } + 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 + 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,true,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. + 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 Sample code : + - 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 img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values). const CImg img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values). @@ -10942,25 +12715,44 @@ namespace cimg_library { std::printf("'img1' and 'img2' have same dimensions and values."); } \endcode - \sa operator!=(). **/ template bool operator==(const CImg& img) const { - const unsigned int siz = size(); - bool vequal = true; + 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; vequal && ptrd>_data; vequal = vequal && ((*(--ptrd))==(*(--ptrs)))) {} - return vequal; + 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 + 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. + 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). - \sa operator==(). **/ template bool operator!=(const CImg& img) const { @@ -10969,13 +12761,14 @@ namespace cimg_library { //! Construct an image list from two images. /** - Return a new list of image (\c CImgList instance) containing exactly two elements : + 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. + \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). + - 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. @@ -10983,9 +12776,9 @@ namespace cimg_library { - 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::insert(const CImg&,unsigned int,bool) or - move_to(CImgList&,unsigned int). - \par Sample code : + Fast insertions of images in an image list are possible with + CImgList::insert(const CImg&,unsigned int,bool) or move_to(CImgList&,unsigned int). + \par Example \code const CImg img1("reference.jpg"), @@ -10995,9 +12788,6 @@ namespace cimg_library { (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3'. \endcode \image html ref_operator_comma.jpg - \sa operator,(const CImgList&) const, - move_to(CImgList&,unsigned int). - CImgList::insert(const CImg&,unsigned int,bool). **/ template CImgList<_cimg_Tt> operator,(const CImg& img) const { @@ -11006,15 +12796,13 @@ namespace cimg_library { //! 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 : + 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. + \param list Input image list that will be appended to the image instance. \note - Similar to operator,(const CImg&) const, except that it takes an image list as an argument. - \sa operator,(const CImg&) const, - CImgList::insert(const CImgList&,unsigned int,bool). **/ template CImgList<_cimg_Tt> operator,(const CImgList& list) const { @@ -11025,17 +12813,16 @@ namespace cimg_library { /** 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') + \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 Sample code : + \par Example \code const CImg img("reference.jpg"); // Load a RGB color image. const CImgList list = (img<'c'); // Get a list of its three R,G,B channels. (img,list).display(); \endcode \image html ref_operator_less.jpg - \sa get_split(char,int) const. **/ CImgList operator<(const char axis) const { return get_split(axis); @@ -11048,20 +12835,19 @@ namespace cimg_library { //@{ //------------------------------------- - //! Get the type of image pixel values as a C string. + //! 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 "unknown" is returned. - \sa value_type. **/ static const char* pixel_type() { return cimg::type::string(); } - //! Get the number of image columns. + //! Return the number of image columns. /** Return the image width, i.e. the image dimension along the X-axis. \note @@ -11070,17 +12856,14 @@ namespace cimg_library { - 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 (*this)._width. - \sa height(), - depth(), - spectrum(), - size(). + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._width. **/ int width() const { return (int)_width; } - //! Get the number of image rows. + //! Return the number of image rows. /** Return the image height, i.e. the image dimension along the Y-axis. \note @@ -11088,17 +12871,14 @@ namespace cimg_library { - 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 (*this)._height. - \sa width(), - depth(), - spectrum(), - size(). + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._height. **/ int height() const { return (int)_height; } - //! Get the number of image slices. + //! Return the number of image slices. /** Return the image depth, i.e. the image dimension along the Z-axis. \note @@ -11108,220 +12888,198 @@ namespace cimg_library { - 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 (*this)._depth. - \sa width(), - height(), - spectrum(), - size(). + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._depth. **/ int depth() const { return (int)_depth; } - //! Get the number of image channels. + //! 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() 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 (*this)._spectrum. - \sa width(), - height(), - depth(), - size(). + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._spectrum. **/ int spectrum() const { return (int)_spectrum; } - //! Get the total number of pixel values. + //! Return the total number of pixel values. /** Return width()*\ref height()*\ref depth()*\ref spectrum(), 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 instance is equal to size()*sizeof(T). - \par Sample code : + - The allocated memory size for a pixel buffer of a non-shared \c CImg instance is equal to + size()*sizeof(T). + \par Example \code const CImg 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 - \sa width(), - height(), - depth(), - spectrum(). **/ - unsigned int size() const { - return _width*_height*_depth*_spectrum; + ulongT size() const { + return (ulongT)_width*_height*_depth*_spectrum; } - //! Get a pointer to the first pixel value. + //! 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 data()+\ref size()-1 (included). - - To get the pointer to one particular location of the pixel buffer, use data(unsigned int,unsigned int,unsigned int,unsigned int) instead. - \sa operator T*() const, - data(unsigned int,unsigned int,unsigned int,unsigned int). + and goes to data()+\ref size() - 1 (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; } - //! Get a pointer to the first pixel value \const. + //! Return a pointer to the first pixel value \const. const T* data() const { return _data; } - //! Get a pointer to a located pixel value. + //! 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, + 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. + \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 &(img(x,y,z,c)). Thus, this method has the same properties as - operator()(unsigned int,unsigned int,unsigned int,unsigned int). - \sa operator()(unsigned int,unsigned int,unsigned int,unsigned int), - data(). + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)). 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 unsigned int off = (unsigned int)offset(x,y,z,c); - if (off>=size()) { + 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].", + "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].", cimg_instance, x,y,z,c,off); - return _data; - } return _data + off; } - //! Get a pointer to a located pixel value \const. + //! 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*>(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 + y*_width + z*_width*_height + c*_width*_height*_depth; + 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 + y*_width + z*_width*_height + c*_width*_height*_depth; + return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; } #endif - //! Get the offset to a located pixel value, with respect to the beginning of the pixel buffer. + //! 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. + \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 &(img(x,y,z,c)) - img.data(). Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). - \par Sample code : + \par Example \code const CImg 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 - \sa operator()(unsigned int,unsigned int,unsigned int,unsigned int), - data(unsigned int,unsigned int,unsigned int,unsigned int). **/ - int offset(const int x, const int y=0, const int z=0, const int c=0) const { - return x + y*_width + z*_width*_height + c*_width*_height*_depth; + 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; } - //! Get a CImg::iterator pointing to the first pixel value. + //! Return a CImg::iterator pointing to the first pixel value. /** \note - Equivalent to data(). - It has been mainly defined for compatibility with STL naming conventions. - \sa data(). **/ iterator begin() { return _data; } - //! Get a CImg::iterator pointing to the first value of the pixel buffer \const. + //! Return a CImg::iterator pointing to the first value of the pixel buffer \const. const_iterator begin() const { return _data; } - //! Get a CImg::iterator pointing next to the last pixel value. + //! Return a CImg::iterator pointing next to the last pixel value. /** \note - Writing \c img.end() is equivalent to img.data() + img.size(). - 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 an - strict upper bound for a CImg::iterator. - \par Sample code : + - 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::iterator. + \par Example \code - CImg img(100,100,1,3); // Define a 100x100 RGB color image. - for (CImg::iterator it = img.begin(); it img(100,100,1,3); // Define a 100x100 RGB color image. + // 'img.end()' used below as an upper bound for the iterator. + for (CImg::iterator it = img.begin(); it::iterator pointing next to the last pixel value \const. + //! Return a CImg::iterator pointing next to the last pixel value \const. const_iterator end() const { return _data + size(); } - //! Get a reference to the first pixel value. + //! Return a reference to the first pixel value. /** \note - Writing \c img.front() is equivalent to img[0], or img(0,0,0,0). - It has been mainly defined for compatibility with STL naming conventions. - \sa data(), - offset(), - begin(). **/ T& front() { return *_data; } - //! Get a reference to the first pixel value \const. + //! Return a reference to the first pixel value \const. const T& front() const { return *_data; } - //! Get a reference to the last pixel value. + //! Return a reference to the last pixel value. /** \note - - Writing \c img.end() is equivalent to img[img.size()-1], or - img(img.width()-1,img.height()-1,img.depth()-1,img.spectrum()-1). + - Writing \c img.end() is equivalent to img[img.size() - 1], or + img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1). - It has been mainly defined for compatibility with STL naming conventions. - \sa data(), - offset(), - end(). **/ T& back() { return *(_data + size() - 1); } - //! Get a reference to the last pixel value \const. + //! Return a reference to the last pixel value \const. const T& back() const { return *(_data + size() - 1); } @@ -11330,24 +13088,21 @@ namespace cimg_library { /** 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. + \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 img[offset], 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. - \sa operator()(), - offset(), - at(int). **/ - T& at(const int offset, const T out_value) { + 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 { + T at(const int offset, const T& out_value) const { return (offset<0 || offset>=(int)size())?out_value:(*this)[offset]; } @@ -11355,74 +13110,66 @@ namespace cimg_library { /** 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. + \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. + - 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). - \sa operator()(), - offset(), - at(int,const T). **/ T& at(const int offset) { if (is_empty()) throw CImgInstanceException(_cimg_instance - "at() : Empty 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]; + 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. - T at(const int offset) const { + const T& at(const int offset) const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "at() : Empty instance.", + "at(): Empty instance.", cimg_instance); return _at(offset); } - T _at(const int offset) const { + 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]; + 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. + \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. + - 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. - \sa operator()(), - at(int,const T). - atX(int,int,int,int), - atXY(int,int,int,int,const T), - atXYZ(int,int,int,int,const T), - atXYZC(int,int,int,int,const T). **/ - T& atX(const int x, const int y, const int z, const int c, const T out_value) { + 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 { + 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); } @@ -11430,60 +13177,55 @@ namespace cimg_library { /** 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. + \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). + - 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. - \sa operator()(), - at(int), - atX(int,int,int,int,const T), - atXY(int,int,int,int), - atXYZ(int,int,int,int), - atXYZC(int,int,int,int). **/ 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.", + "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); + 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. - T atX(const int x, const int y=0, const int z=0, const int c=0) 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.", + "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) const { - return (*this)(x<0?0:(x>=width()?width()-1: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) { + 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 { + 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); } @@ -11491,44 +13233,48 @@ namespace cimg_library { /** 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). + - 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.", + "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)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c); + 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. - T atXY(const int x, const int y, const int z=0, const int c=0) 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.", + "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) const { - return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1: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. + 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) { + 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 { + 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); } @@ -11536,139 +13282,139 @@ namespace cimg_library { /** 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). + - 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.", + "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)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y), - z<0?0:(z>=depth()?depth()-1:z),c); + 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. - T atXYZ(const int x, const int y, const int z, const int c=0) 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.", + "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) const { - return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y), - z<0?0:(z>=depth()?depth()-1: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. + 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) { + 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); + 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). + - 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.", + "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)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y), - z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1: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. - T atXYZC(const int x, const int y, const int z, const int c) 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.", + "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) const { - return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y), - z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1: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)); } - //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate. + //! 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. + \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. + - 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. - \sa operator()(), - atX(int,int,int,int,const T), - linear_atX(float,int,int,int) const, - linear_atXY(float,float,int,int,const T) const, - linear_atXYZ(float,float,float,int,const T) const, - linear_atXYZC(float,float,float,float,const T) const. **/ - Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_value) const { + 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 Ic + dx*(In - Ic); } - //! Get pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate. + //! 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. + 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). + - 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. - \sa operator()(), - atX(int,int,int,int), - linear_atX(float,int,int,int,const T) const, - linear_atXY(float,float,int,int) const, - linear_atXYZ(float,float,float,int) const, - linear_atXYZC(float,float,float,float) const. **/ 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.", + "linear_atX(): Empty instance.", cimg_instance); return _linear_atX(fx,y,z,c); @@ -11676,24 +13422,24 @@ namespace cimg_library { Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { const float - nfx = fx<0?0:(fx>_width-1?_width-1:fx); + 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; + 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 Ic + dx*(In - Ic); } - //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + //! 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. + 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 { + 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; @@ -11703,20 +13449,21 @@ namespace cimg_library { 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 Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc); } - //! Get pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates. + //! 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). + - 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.", + "linear_atXY(): Empty instance.", cimg_instance); return _linear_atXY(fx,fy,z,c); @@ -11724,8 +13471,8 @@ namespace cimg_library { Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { const float - nfx = fx<0?0:(fx>_width-1?_width-1:fx), - nfy = fy<0?0:(fy>_height-1?_height-1:fy); + 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; @@ -11733,20 +13480,20 @@ namespace cimg_library { dx = nfx - x, dy = nfy - y; const unsigned int - nx = dx>0?x+1:x, - ny = dy>0?y+1:y; + 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 Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc); } - //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + //! 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. + 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 { + 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, @@ -11761,26 +13508,27 @@ namespace cimg_library { 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); + 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); } - //! Get pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + //! 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). + - 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.", + "linear_atXYZ(): Empty instance.", cimg_instance); return _linear_atXYZ(fx,fy,fz,c); @@ -11788,9 +13536,9 @@ namespace cimg_library { Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { const float - nfx = fx<0?0:(fx>_width-1?_width-1:fx), - nfy = fy<0?0:(fy>_height-1?_height-1:fy), - nfz = fz<0?0:(fz>_depth-1?_depth-1:fz); + 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, @@ -11800,30 +13548,30 @@ namespace cimg_library { 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; + 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); + 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); } - //! Get pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z and C-coordinates. + //! 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. + 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 { + 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, @@ -11844,34 +13592,36 @@ namespace cimg_library { 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); + 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); } - //! Get pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates. + //! 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). + - 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.", + "linear_atXYZC(): Empty instance.", cimg_instance); return _linear_atXYZC(fx,fy,fz,fc); @@ -11879,10 +13629,10 @@ namespace cimg_library { Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { const float - nfx = fx<0?0:(fx>_width-1?_width-1:fx), - nfy = fy<0?0:(fy>_height-1?_height-1:fy), - nfz = fz<0?0:(fz>_depth-1?_depth-1:fz), - nfc = fc<0?0:(fc>_spectrum-1?_spectrum-1:fc); + 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, @@ -11894,10 +13644,10 @@ namespace cimg_library { 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; + 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), @@ -11908,46 +13658,42 @@ namespace cimg_library { 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); + 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); } - //! Get pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + //! 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. - \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. + 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. + - 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. - \sa operator()(), - atX(int,int,int,int,const T), - linear_atX(float,int,int,int,const T) const, - cubic_atX(float,int,int,int) const, - cubic_atXY(float,float,int,int,const T) const, - cubic_atXYZ(float,float,float,int,const T) const. **/ - Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value) const { + 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 @@ -11955,65 +13701,64 @@ namespace cimg_library { 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 Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); } - //! Get damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + //! Return damped 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 you can specify the authorized minimum and maximum of the returned value. + Similar to cubic_atX(float,int,int,int,const T) const, except that you can specify the authorized minimum + and maximum of the returned value. **/ - Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value, + Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value, const Tfloat min_value, const Tfloat max_value) const { const Tfloat val = cubic_atX(fx,y,z,c,out_value); return valmax_value?max_value:val; } - //! Get pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + //! 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. - \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. + 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). + - 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. - \sa operator()(), - atX(int,int,int,int), - linear_atX(float,int,int,int) const, - cubic_atX(float,int,int,int,const T) const, - cubic_atXY(float,float,int,int) const, - cubic_atXYZ(float,float,float,int) const. **/ 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.", + "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 = fx<0?0:(fx>_width-1?_width-1:fx); + nfx = 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; + 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 Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); } - //! Get damped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. /** - Similar to cubic_atX(float,int,int,int) const, except that you can specify the authorized minimum and maximum of the returned value. + Similar to cubic_atX(float,int,int,int) const, except that you can specify the authorized minimum and maximum + of the returned value. **/ Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const Tfloat min_value, const Tfloat max_value) const { @@ -12027,77 +13772,88 @@ namespace cimg_library { return valmax_value?max_value:val; } - //! Get pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + //! 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 { + 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)); + 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)); } - //! Get damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + //! Return damped 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 you can specify the authorized minimum and maximum of the returned value. + Similar to cubic_atXY(float,float,int,int,const T) const, except that you can specify the authorized + minimum and maximum of the returned value. **/ - Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value, + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value, const Tfloat min_value, const Tfloat max_value) const { const Tfloat val = cubic_atXY(fx,fy,z,c,out_value); return valmax_value?max_value:val; } - //! Get pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. + //! 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). + - 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.", + "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 = fx<0?0:(fx>_width-1?_width-1:fx), - nfy = fy<0?0:(fy>_height-1?_height-1:fy); + nfx = cimg::cut(fx,0,width() - 1), + nfy = 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+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; + 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; 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)); + 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)); } - //! Get damped pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. + //! Return damped 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 you can specify the authorized minimum and maximum of the returned value. + Similar to cubic_atXY(float,float,int,int) const, except that you can specify the authorized minimum and + maximum of the returned value. **/ Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const Tfloat min_value, const Tfloat max_value) const { @@ -12111,12 +13867,12 @@ namespace cimg_library { return valmax_value?max_value:val; } - //! Get pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + //! 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 { + 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, @@ -12125,154 +13881,197 @@ namespace cimg_library { 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)); + 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)); } - //! Get damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + //! Return damped 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 you can specify the authorized minimum and maximum of the returned value. + Similar to cubic_atXYZ(float,float,float,int,const T) const, except that you can specify the authorized + minimum and maximum of the returned value. **/ - Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value, + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value, const Tfloat min_value, const Tfloat max_value) const { const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_value); return valmax_value?max_value:val; } - //! Get pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + //! 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). + - 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.", + "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 = fx<0?0:(fx>_width-1?_width-1:fx), - nfy = fy<0?0:(fy>_height-1?_height-1:fy), - nfz = fz<0?0:(fz>_depth-1?_depth-1:fz); + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1), + nfz = 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; + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)), + 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)); + 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)); } - //! Get damped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates. /** - Similar to cubic_atXYZ(float,float,float,int) const, except that you can specify the authorized minimum and maximum of the returned value. + Similar to cubic_atXYZ(float,float,float,int) const, except that you can specify the authorized minimum and + maximum of the returned value. **/ Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const Tfloat min_value, const Tfloat max_value) const { @@ -12286,21 +14085,44 @@ namespace cimg_library { return valmax_value?max_value:val; } - //! Set pixel value, using linear interpolation for the X and Y-coordinates. + //! Set pixel value, using linear interpolation for the X-coordinates. /** - Set pixel value at specified coordinates (\c fx,\c fy,\c z,\c c) in the image instance, in a way that the value is spread - amongst several neighbors if the pixel coordinates are indeed float-valued. - \param value : Pixel value to set. - \param fx : X-coordinate of the pixel value (float-valued). - \param fy : Y-coordinate of the pixel value (float-valued). - \param z : Z-coordinate of the pixel value. - \param c : C-coordinate of the pixel value. - \param is_added : Boolean telling if the pixel value is added to (\c true), or simply replace (\c false) the current image pixel(s). + 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 - - If specified coordinates are outside image bounds, no operations are performed. - \sa linear_atXY(), - set_linear_atXYZ(). + - Calling this method with out-of-bounds coordinates does nothing. + **/ + CImg& 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=0 && z=0 && c=0 && x=0 && nx& 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) { @@ -12313,21 +14135,21 @@ namespace cimg_library { if (z>=0 && z=0 && c=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && z=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && nz=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx 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. + \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. + \param format For float-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 "". - 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 max_size + 1. - \sa pixel_type(). **/ - CImg value_string(const char separator=',', const unsigned int max_size=0) const { - if (is_empty()) return CImg(1,1,1,1,0); + CImg value_string(const char separator=',', const unsigned int max_size=0, + const char *const format=0) const { + if (is_empty()) return CImg::string(""); CImgList items; - char s_item[256] = { 0 }; + CImg s_item(256); *s_item = 0; const T *ptrs = _data; unsigned int string_size = 0; - for (unsigned int off = 0, siz = (unsigned int)size(); off::format(),cimg::type::format(*(ptrs++))); - CImg item(s_item,printed_size); - item[printed_size-1] = separator; + const char *const _format = format?format:cimg::type::format(); + for (ulongT off = 0, siz = size(); off::format(*(ptrs++))); + CImg item(s_item._data,printed_size); + item[printed_size - 1] = separator; item.move_to(items); if (max_size) string_size+=printed_size; } @@ -12447,18 +14273,6 @@ namespace cimg_library { - A shared image do not own his pixel buffer data() and will not deallocate it on destruction. - Most of the time, a \c CImg 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). - \sa CImg(const t *const,unsigned int,unsigned int,unsigned int,unsigned int,bool), - CImg(const CImg&), - CImg(const CImg&,bool), - assign(const t *const,unsigned int,unsigned int,unsigned int,unsigned int,bool), - get_shared_points(), - get_shared_line(), - get_shared_lines(), - get_shared_plane(), - get_shared_planes(), - get_shared_channel(), - get_shared_channels(), - get_shared(). **/ bool is_shared() const { return _is_shared; @@ -12466,123 +14280,80 @@ namespace cimg_library { //! 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. - \sa CImg(), - assign(). + 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 width is equal to a specified value. + //! Test if image instance contains a 'inf' value. /** - Return \c true if image instance has \c size_x columns, and \c false otherwise. - \param size_x : Desired width to test with. - \note - - Return the boolean width()==size_x. - \sa is_sameX(const CImg&) const, - is_sameX(const CImgDisplay&) const, - is_sameY(unsigned int) const, - is_sameZ(unsigned int) const, - is_sameC(unsigned int) const, - is_sameXY(unsigned int,unsigned int) const, - is_sameXYZ(unsigned int,unsigned int,unsigned int) const, - is_sameXYZC(unsigned int,unsigned int,unsigned int,unsigned int) const. + Return \c true, if image instance contains a 'inf' value, and \c false otherwise. **/ - bool is_sameX(const unsigned int size_x) const { - return (_width==size_x); + bool is_inf() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_inf((float)*p)) return true; + return false; } - //! Test if image width is the same as that of another image. + //! Test if image instance contains a NaN value. /** - Return \c true if image instance has \c img.width() columns, and \c false otherwise. - \param img : Input image to test width() with. - \note - - Return the boolean width()==img.width(). - \sa is_sameX(unsigned int) const, - is_sameX(const CImgDisplay&) const, - is_sameY(const CImg&) const, - is_sameZ(const CImg&) const, - is_sameC(const CImg&) const, - is_sameXY(const CImg&) const, - is_sameXYZ(const CImg&) const, - is_sameXYZC(const CImg&) const. + Return \c true, if image instance contains a NaN value, and \c false otherwise. **/ + bool is_nan() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::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 bool is_sameX(const CImg& img) const { return is_sameX(img._width); } - //! Test if image width is the same as that of an existing display window. - /** - Return \c true if image instance has \c disp.width() columns, and \c false otherwise. - \param disp : Input display window to test width() with. - \note - - Return the boolean width()==disp.width(). - \sa is_sameX(unsigned int) const, - is_sameX(const CImg&) const, - is_sameY(const CImgDisplay&) const, - is_sameXY(const CImgDisplay&) const, - **/ + //! Test if image width is equal to specified value. bool is_sameX(const CImgDisplay& disp) const { - return is_sameX((unsigned int)disp.width()); + return is_sameX(disp._width); } - //! Test if image height is equal to a specified value. - /** - Similar to is_sameX(unsigned int) const, except that the test focuses on the image height(). - **/ + //! Test if image height is equal to specified value. bool is_sameY(const unsigned int size_y) const { - return (_height==size_y); + return _height==size_y; } - //! Test if image height is the same as that of another image. - /** - Similar to is_sameX(const CImg&) const, except that the test focuses on the image height(). - **/ + //! Test if image height is equal to specified value. template bool is_sameY(const CImg& img) const { return is_sameY(img._height); } - //! Test if image height is the same as that of an existing display window. - /** - Similar to is_sameX(const CImgDisplay&) const, except that the test focuses on the image height(). - **/ + //! Test if image height is equal to specified value. bool is_sameY(const CImgDisplay& disp) const { - return is_sameY((unsigned int)disp.height()); + return is_sameY(disp._height); } - //! Test if image depth is equal to a specified value. - /** - Similar to is_sameX(unsigned int) const, except that the test focuses on the image depth(). - **/ + //! Test if image depth is equal to specified value. bool is_sameZ(const unsigned int size_z) const { - return (_depth==size_z); + return _depth==size_z; } - //! Test if image depth is the same as that of another image. - /** - Similar to is_sameX(const CImg&) const, except that the test focuses on the image depth(). - **/ + //! Test if image depth is equal to specified value. template bool is_sameZ(const CImg& img) const { return is_sameZ(img._depth); } - //! Test if image spectrum is equal to a specified value. - /** - Similar to is_sameX(unsigned int) const, except that the test focuses on the image spectrum(). - **/ + //! Test if image spectrum is equal to specified value. bool is_sameC(const unsigned int size_c) const { - return (_spectrum==size_c); + return _spectrum==size_c; } - //! Test if image spectrum is the same as that of another image. - /** - Similar to is_sameX(const CImg&) const, except that the test focuses on the image spectrum(). - **/ + //! Test if image spectrum is equal to specified value. template bool is_sameC(const CImg& img) const { return is_sameC(img._spectrum); @@ -12593,7 +14364,7 @@ namespace cimg_library { 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 (is_sameX(size_x) && is_sameY(size_y)); + return _width==size_x && _height==size_y; } //! Test if image width and height are the same as that of another image. @@ -12602,7 +14373,7 @@ namespace cimg_library { **/ template bool is_sameXY(const CImg& img) const { - return (is_sameX(img) && is_sameY(img)); + return is_sameXY(img._width,img._height); } //! Test if image width and height are the same as that of an existing display window. @@ -12610,7 +14381,7 @@ namespace cimg_library { 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_sameX(disp) && is_sameY(disp)); + return is_sameXY(disp._width,disp._height); } //! Test if image width and depth are equal to specified values. @@ -12618,7 +14389,7 @@ namespace cimg_library { 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 (is_sameX(size_x) && is_sameZ(size_z)); + return _width==size_x && _depth==size_z; } //! Test if image width and depth are the same as that of another image. @@ -12627,7 +14398,7 @@ namespace cimg_library { **/ template bool is_sameXZ(const CImg& img) const { - return (is_sameX(img) && is_sameZ(img)); + return is_sameXZ(img._width,img._depth); } //! Test if image width and spectrum are equal to specified values. @@ -12635,7 +14406,7 @@ namespace cimg_library { 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 (is_sameX(size_x) && is_sameC(size_c)); + return _width==size_x && _spectrum==size_c; } //! Test if image width and spectrum are the same as that of another image. @@ -12644,7 +14415,7 @@ namespace cimg_library { **/ template bool is_sameXC(const CImg& img) const { - return (is_sameX(img) && is_sameC(img)); + return is_sameXC(img._width,img._spectrum); } //! Test if image height and depth are equal to specified values. @@ -12652,7 +14423,7 @@ namespace cimg_library { 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 (is_sameY(size_y) && is_sameZ(size_z)); + return _height==size_y && _depth==size_z; } //! Test if image height and depth are the same as that of another image. @@ -12661,7 +14432,7 @@ namespace cimg_library { **/ template bool is_sameYZ(const CImg& img) const { - return (is_sameY(img) && is_sameZ(img)); + return is_sameYZ(img._height,img._depth); } //! Test if image height and spectrum are equal to specified values. @@ -12669,7 +14440,7 @@ namespace cimg_library { 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 (is_sameY(size_y) && is_sameC(size_c)); + return _height==size_y && _spectrum==size_c; } //! Test if image height and spectrum are the same as that of another image. @@ -12678,7 +14449,7 @@ namespace cimg_library { **/ template bool is_sameYC(const CImg& img) const { - return (is_sameY(img) && is_sameC(img)); + return is_sameYC(img._height,img._spectrum); } //! Test if image depth and spectrum are equal to specified values. @@ -12686,7 +14457,7 @@ namespace cimg_library { 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 (is_sameZ(size_z) && is_sameC(size_c)); + return _depth==size_z && _spectrum==size_c; } //! Test if image depth and spectrum are the same as that of another image. @@ -12695,7 +14466,7 @@ namespace cimg_library { **/ template bool is_sameZC(const CImg& img) const { - return (is_sameZ(img) && is_sameC(img)); + return is_sameZC(img._depth,img._spectrum); } //! Test if image width, height and depth are equal to specified values. @@ -12703,7 +14474,7 @@ namespace cimg_library { 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) && is_sameZ(size_z)); + 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. @@ -12712,7 +14483,7 @@ namespace cimg_library { **/ template bool is_sameXYZ(const CImg& img) const { - return (is_sameXY(img) && is_sameZ(img)); + return is_sameXYZ(img._width,img._height,img._depth); } //! Test if image width, height and spectrum are equal to specified values. @@ -12720,7 +14491,7 @@ namespace cimg_library { 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) && is_sameC(size_c)); + 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. @@ -12729,7 +14500,7 @@ namespace cimg_library { **/ template bool is_sameXYC(const CImg& img) const { - return (is_sameXY(img) && is_sameC(img)); + return is_sameXYC(img._width,img._height,img._spectrum); } //! Test if image width, depth and spectrum are equal to specified values. @@ -12737,7 +14508,7 @@ namespace cimg_library { 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) && is_sameC(size_c)); + 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. @@ -12746,7 +14517,7 @@ namespace cimg_library { **/ template bool is_sameXZC(const CImg& img) const { - return (is_sameXZ(img) && is_sameC(img)); + return is_sameXZC(img._width,img._depth,img._spectrum); } //! Test if image height, depth and spectrum are equal to specified values. @@ -12754,7 +14525,7 @@ namespace cimg_library { 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) && is_sameC(size_c)); + 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. @@ -12763,15 +14534,17 @@ namespace cimg_library { **/ template bool is_sameYZC(const CImg& img) const { - return (is_sameYZ(img) && is_sameC(img)); + 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. + 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) && is_sameC(size_c)); + 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. @@ -12780,28 +14553,24 @@ namespace cimg_library { **/ template bool is_sameXYZC(const CImg& img) const { - return (is_sameXYZ(img) && is_sameC(img)); + 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. + 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 : + - Return \c true only if all these conditions are verified: - The image instance is \e not empty. - - 0<=x<=\ref width()-1. - - 0<=y<=\ref height()-1. - - 0<=z<=\ref depth()-1. - - 0<=c<=\ref spectrum()-1. - \sa contains(const T&,t&,t&,t&,t&) const, - contains(const T&,t&,t&,t&) const, - contains(const T&,t&,t&) const, - contains(const T&,t&) const, - contains(const T&) const. + - 0<=x<=\ref width() - 1. + - 0<=y<=\ref height() - 1. + - 0<=z<=\ref depth() - 1. + - 0<=c<=\ref spectrum() - 1. **/ bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const { return !is_empty() && x>=0 && x=0 && y=0 && z=0 && c img(100,100,1,3); // Construct a 100x100 RGB color image. - const unsigned int offset = 1249; // Offset to the pixel (49,12,0,0). + 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 - \sa containsXYZC(int,int,int,int) const, - contains(const T&,t&,t&,t&) const, - contains(const T&,t&,t&) const, - contains(const T&,t&) const, - contains(const T&) const. **/ template bool contains(const T& pixel, t& x, t& y, t& z, t& c) const { - const unsigned int wh = _width*_height, whd = wh*_depth, siz = whd*_spectrum; + 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; - unsigned int off = (unsigned int)(ppixel - _data); - const unsigned int nc = off/whd; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = (ulongT)(ppixel - _data); + const ulongT nc = off/whd; off%=whd; - const unsigned int nz = off/wh; + const ulongT nz = off/wh; off%=wh; - const unsigned int ny = off/_width, nx = off%_width; + const ulongT ny = off/_width, nx = off%_width; x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc; return true; } @@ -12853,13 +14618,13 @@ namespace cimg_library { **/ template bool contains(const T& pixel, t& x, t& y, t& z) const { - const unsigned int wh = _width*_height, whd = wh*_depth, siz = whd*_spectrum; + 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; - unsigned int off = ((unsigned int)(ppixel - _data))%whd; - const unsigned int nz = off/wh; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = ((ulongT)(ppixel - _data))%whd; + const ulongT nz = off/wh; off%=wh; - const unsigned int ny = off/_width, nx = off%_width; + const ulongT ny = off/_width, nx = off%_width; x = (t)nx; y = (t)ny; z = (t)nz; return true; } @@ -12870,11 +14635,11 @@ namespace cimg_library { **/ template bool contains(const T& pixel, t& x, t& y) const { - const unsigned int wh = _width*_height, siz = wh*_depth*_spectrum; + 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; - unsigned int off = ((unsigned int)(ppixel - _data))%wh; - const unsigned int ny = off/_width, nx = off%_width; + 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; } @@ -12886,8 +14651,8 @@ namespace cimg_library { template 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)(((unsigned int)(ppixel - _data))%_width); + if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false; + x = (t)(((ulongT)(ppixel - _data))%_width); return true; } @@ -12902,26 +14667,26 @@ namespace cimg_library { //! 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. + 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 instances are \e non-shared and do not overlap between each others. - \par Sample code : + \par Example \code const CImg 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"); + std::printf("Buffers overlap!\n"); } \endcode - \sa is_shared(). **/ template bool is_overlapped(const CImg& img) const { - const unsigned int csiz = size(), isiz = img.size(); + const ulongT csiz = size(), isiz = img.size(); return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz)); } @@ -12929,33 +14694,32 @@ namespace cimg_library { /** 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 is_full_check : Boolean telling 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. + \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 is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of + - 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. - \sa is_CImg3d(), - draw_object3d(), - display_object3d(). **/ template bool is_object3d(const CImgList& primitives, const CImgList& colors, const to& opacities, - const bool is_full_check=true, + 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) std::sprintf(error_message, - "3d object has no vertices but %u primitives, %u colors and %u opacities", - primitives._width,colors._width,opacities.size()); + 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; @@ -12963,35 +14727,36 @@ namespace cimg_library { // Check consistency of vertices. if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions. - if (error_message) std::sprintf(error_message, - "3d object (%u,%u) has invalid vertices dimensions (%u,%u,%u,%u)", + 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) std::sprintf(error_message, + 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) std::sprintf(error_message, - "3d object (%u,%u) defines %u opacities", - _width,primitives._width,opacities.size()); + if (error_message) cimg_sprintf(error_message, + "3d object (%u,%u) defines %lu opacities", + _width,primitives._width,(unsigned long)opacities.size()); return false; } - if (!is_full_check) return true; + if (!full_check) return true; // Check consistency of primitives. cimglist_for(primitives,l) { const CImg& primitive = primitives[l]; - const unsigned int psiz = primitive.size(); + 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) std::sprintf(error_message, - "3d object (%u,%u) refers to invalid vertex indice %u in point primitive %u", + 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; } @@ -13001,8 +14766,9 @@ namespace cimg_library { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) std::sprintf(error_message, - "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u", + 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; } @@ -13013,8 +14779,9 @@ namespace cimg_library { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) std::sprintf(error_message, - "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u", + 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; } @@ -13026,8 +14793,9 @@ namespace cimg_library { i1 = (unsigned int)primitive(1), i2 = (unsigned int)primitive(2); if (i0>=_width || i1>=_width || i2>=_width) { - if (error_message) std::sprintf(error_message, - "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u", + 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; } @@ -13040,16 +14808,17 @@ namespace cimg_library { i2 = (unsigned int)primitive(2), i3 = (unsigned int)primitive(3); if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) { - if (error_message) std::sprintf(error_message, - "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u", + 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) std::sprintf(error_message, - "3d object has invalid primitive %u of size %u", - l,psiz); + 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; } } @@ -13058,9 +14827,9 @@ namespace cimg_library { cimglist_for(colors,c) { const CImg& color = colors[c]; if (!color) { - if (error_message) std::sprintf(error_message, - "3d object has empty color for primitive %u", - c); + if (error_message) cimg_sprintf(error_message, + "3d object (%u,%u) defines no color for primitive [%u]", + _width,primitives._width,c); return false; } } @@ -13069,9 +14838,10 @@ namespace cimg_library { if (colors._width>primitives._width) { const CImg &light = colors.back(); if (!light || light._depth>1) { - if (error_message) std::sprintf(error_message, - "3d object has invalid light texture (%u,%u,%u,%u)", - light._width,light._height,light._depth,light._spectrum); + 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; } } @@ -13082,23 +14852,19 @@ namespace cimg_library { //! 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 is_full_check : Boolean telling 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. + \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 is_full_checking to \c false to speed-up the 3d object checking. In this case, only the size of + - 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. - \sa is_object3d(), - object3dtoCImg3d(const CImgList&,const CImgList&,const to&), - draw_object3d(), - display_object3d(). **/ - bool is_CImg3d(const bool is_full_check=true, char *const error_message=0) const { + 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) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d has invalid dimensions (%u,%u,%u,%u)", _width,_height,_depth,_spectrum); return false; @@ -13106,7 +14872,7 @@ namespace cimg_library { 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) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d header not found"); return false; } @@ -13114,46 +14880,60 @@ namespace cimg_library { 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) std::sprintf(error_message, - "CImg3d has no vertices but %u primitives", - 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) std::sprintf(error_message, - "CImg3d (%u,%u) is empty but contains %u byte%s more than expected", - nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":""); + 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; } - ptrs+=3*nb_points; - if (ptrs>ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) has incomplete vertex data", - nb_points,nb_primitives); + 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; } - if (!is_full_check) return true; + ptrs+=3*nb_points; // Check consistency of primitive data. if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) has no primitive data", - nb_points,nb_primitives); + 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_points) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive %u", + 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; } @@ -13164,8 +14944,9 @@ namespace cimg_library { i1 = cimg::float2uint((float)*(ptrs++)); ptrs+=3; if (i0>=nb_points || i1>=nb_points) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u", + 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; } @@ -13176,8 +14957,9 @@ namespace cimg_library { i1 = cimg::float2uint((float)*(ptrs++)); if (nb_inds==6) ptrs+=4; if (i0>=nb_points || i1>=nb_points) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u", + 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; } @@ -13189,8 +14971,9 @@ namespace cimg_library { i2 = cimg::float2uint((float)*(ptrs++)); if (nb_inds==9) ptrs+=6; if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u", + 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; } @@ -13203,76 +14986,87 @@ namespace cimg_library { 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) std::sprintf(error_message, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u", + 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) std::sprintf(error_message, - "CImg3d (%u,%u) has invalid primitive %u of size %u", + 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) std::sprintf(error_message, - "CImg3d (%u,%u) has incomplete primitive data for primitive %u", - nb_points,nb_primitives,p); + 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) std::sprintf(error_message, - "CImg3d (%u,%u) has no color/texture data", + 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=c) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u for primitive %u", + 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) std::sprintf(error_message, - "CImg3d (%u,%u) has incomplete color/texture data for primitive %u", - nb_points,nb_primitives,c); + 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) std::sprintf(error_message, - "CImg3d (%u,%u) has no opacity data", + 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=o) { - if (error_message) std::sprintf(error_message, - "CImg3d (%u,%u) refers to invalid shared opacity indice %u for primitive %u", + 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) std::sprintf(error_message, - "CImg3d (%u,%u) has incomplete opacity data for primitive %u", + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", nb_points,nb_primitives,o); return false; } @@ -13280,16 +15074,16 @@ namespace cimg_library { // Check end of data. if (ptrs1?"s":""); + 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); + return val>=(T)c && val<(T)(c + 1); } //@} @@ -13299,742 +15093,7907 @@ namespace cimg_library { //@{ //------------------------------------- - // Define the math formula parser/compiler and evaluator. + // Define the math formula parser/compiler and expression evaluator. struct _cimg_math_parser { - CImgList label; - CImgList code; - CImg level, opcode; CImg mem; - CImg expr; - const CImg& reference; - CImg reference_stats; - unsigned int mempos, result; - const char *const calling_function; -#define _cimg_mp_return(x) { *se = saved_char; return x; } -#define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op)); -#define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1)); -#define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); } -#define _cimg_mp_opcode3(op,i1,i2,i3) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3; _cimg_mp_return(opcode3(op,_i1,_i2,_i3)); } -#define _cimg_mp_opcode5(op,i1,i2,i3,i4,i5) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5; \ - _cimg_mp_return(opcode5(op,_i1,_i2,_i3,_i4,_i5)); } + CImg memtype; + CImgList _code, &code, code_init, code_end; + CImg opcode; + const CImg *p_code_end, *p_code; + const CImg *const p_break; - // Constructor - Destructor. - _cimg_math_parser(const CImg& img, const char *const expression, const char *const funcname=0): - reference(img),calling_function(funcname?funcname:"cimg_math_parser") { - unsigned int l = 0; - if (expression) { - l = std::strlen(expression); - expr.assign(expression,l+1); - if (*expr._data) { - char *d = expr._data; - for (const char *s = expr._data; *s || (bool)(*d=0); ++s) if (*s!=' ') *(d++) = *s; - l = d - expr._data; + CImg expr, pexpr; + const CImg& imgin; + const CImgList& listin; + CImg &imgout; + CImgList& listout; + + CImg _img_stats, &img_stats, constcache_vals; + CImgList _list_stats, &list_stats, _list_median, &list_median; + CImg mem_img_stats, constcache_inds; + + CImg level, variable_pos, reserved_label; + CImgList variable_def, macro_def, macro_body; + CImgList 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; + 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_vector_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Vector size +#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_vector0(dim) check_vector0(dim,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_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. + _cimg_math_parser(const char *const expression, const char *const funcname=0, + const CImg& img_input=CImg::const_empty(), CImg *const img_output=0, + const CImgList *const list_input=0, CImgList *const list_output=0, + const bool _is_fill=false): + code(_code),p_break((CImg*)0 - 2), + imgin(img_input),listin(list_input?*list_input:CImgList::const_empty()), + imgout(img_output?*img_output:CImg::empty()),listout(list_output?*list_output:CImgList::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), + calling_function(funcname?funcname:"cimg_math_parser") { + if (!expression || !*expression) + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: Empty expression.", + pixel_type(),_cimg_mp_calling_function); + const char *_expression = expression; + while (*_expression && ((signed char)*_expression<=' ' || *_expression==';')) ++_expression; + CImg::string(_expression).move_to(expr); + char *ps = &expr.back() - 1; + while (ps>expr._data && ((signed char)*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 ((signed char)*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.0); // e + mem[28] = cimg::PI; // pi + mem[_cimg_mp_slot_nan] = cimg::type::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); + if (!_cimg_mp_is_constant(ind_result)) { + if (_cimg_mp_is_vector(ind_result)) + CImg(&mem[ind_result] + 1,_cimg_mp_vector_size(ind_result),1,1,1,true). + fill(cimg::type::nan()); + else mem[ind_result] = cimg::type::nan(); + } + + // Free resources used for compiling expression and prepare evaluation. + result_dim = _cimg_mp_vector_size(ind_result); + 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 init() bloc if any specified. + if (code_init) { + 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_init.end(); + for (p_code = code_init; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); } } - if (!l) throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s() : Empty specified expression.", - pixel_type(),calling_function); + p_code_end = code.end(); + } - int lv = 0; // Count parenthesis level of expression. - level.assign(l); - unsigned int *pd = level._data; - for (const char *ps = expr._data; *ps && lv>=0; ++ps) *(pd++) = (unsigned int)(*ps=='('?lv++:*ps==')'?--lv:lv); - if (lv!=0) { + _cimg_math_parser(): + code(_code),p_code_end(0),p_break((CImg*)0 - 2), + imgin(CImg::const_empty()),listin(CImgList::const_empty()), + imgout(CImg::empty()),listout(CImgList::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), + 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)),calling_function(0) { +#ifdef cimg_use_openmp + mem[17] = omp_get_thread_num(); +#endif + opcode.assign(); + opcode._is_shared = true; + } + + // Count parentheses/brackets level of each character of the expression. + CImg get_level(CImg& 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 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_math_parser] " - "CImg<%s>::%s() : Unbalanced parentheses in specified expression '%s'.", - pixel_type(),calling_function, + "CImg<%s>::%s: Unterminated string literal, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, expr._data); } - // Init constant values. - mem.assign(512); - label.assign(512); - mem[0] = 0; - mem[1] = 1; - mem[2] = (double)reference._width; - mem[3] = (double)reference._height; - mem[4] = (double)reference._depth; - mem[5] = (double)reference._spectrum; - mem[6] = cimg::PI; - mem[7] = std::exp(1.0); // Then [8] = x, [9] = y, [10] = z, [11] = c - mempos = 12; - result = compile(expr._data,expr._data+l); // Compile formula into a serie of opcodes. + if (level) { + cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, + expr._data); + } + return res; } - // Insert code instructions. - unsigned int opcode0(const char op) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(op,pos).move_to(code); - return pos; - } - - unsigned int opcode1(const char op, const unsigned int arg1) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(op,pos,arg1).move_to(code); - return pos; - } - - unsigned int opcode2(const char op, const unsigned int arg1, const unsigned int arg2) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(op,pos,arg1,arg2).move_to(code); - return pos; - } - - unsigned int opcode3(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(op,pos,arg1,arg2,arg3).move_to(code); - return pos; - } - - unsigned int opcode5(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, - const unsigned int arg4, const unsigned int arg5) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code); - return pos; + // Tell for each character of an expression if it is inside a string or not. + CImg is_inside_string(CImg& 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 res = CImg::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 *const ss, char *const se) { - if (!ss || se<=ss || !*ss) { + unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref) { + if (depth>256) { + cimg::strellipsize(expr,64); throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s() : Missing item in specified expression '%s'.", - pixel_type(),calling_function, - expr._data); + "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 (ssss && ((signed char)(c1 = *(se - 1))<=' ' || c1==';')) --se; + } + while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { + ++ss; --se; c2 = 1; + } + } while (c2 && ss::%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 se4 = se-4, - *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 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, 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; - if (*se1==';') return compile(ss,se1); + const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; + bool is_sth, is_relative; + CImg ref; + CImgList _opcode; + CImg variable_name; - // Look for a single value, variable or variable assignment. - char end = 0, sep = 0; double val = 0; - const int nb = std::sscanf(ss,"%lf%c%c",&val,&sep,&end); - if (nb==1) { - if (val==0) _cimg_mp_return(0); - if (val==1) _cimg_mp_return(1); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = val; - _cimg_mp_return(pos); + // Look for a single value or a pre-defined variable. + int nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); + +#if cimg_OS==2 + // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able + // to read those particular values. + if (!nb && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) { + is_sth = true; + s = ss; + if (*s=='+') ++s; else if (*s=='-') { ++s; is_sth = false; } + if (!cimg::strcasecmp(s,"inf")) { val = cimg::type::inf(); nb = 1; } + else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type::nan(); nb = 1; } + if (nb==1 && !is_sth) val = -val; } - if (nb==2 && sep=='%') { - if (val==0) _cimg_mp_return(0); - if (val==100) _cimg_mp_return(1); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = val/100; - _cimg_mp_return(pos); - } - if (ss1==se) switch (*ss) { - case 'w' : _cimg_mp_return(2); case 'h' : _cimg_mp_return(3); case 'd' : _cimg_mp_return(4); case 's' : _cimg_mp_return(5); - case 'x' : _cimg_mp_return(8); case 'y' : _cimg_mp_return(9); case 'z' : _cimg_mp_return(10); case 'c' : _cimg_mp_return(11); - case 'e' : _cimg_mp_return(7); - case 'u' : case '?' : _cimg_mp_opcode2(0,0,1); - case 'g' : _cimg_mp_opcode0(1); - case 'i' : _cimg_mp_opcode0(2); +#endif + 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']); + _cimg_mp_check_vector0(imgin._spectrum); + need_input_copy = true; + pos = vector(imgin._spectrum); + CImg::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); } - if (ss1==se1) { - if (*ss=='p' && *ss1=='i') _cimg_mp_return(6); // pi + 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=='m') _cimg_mp_opcode0(57); // im - if (*ss1=='M') _cimg_mp_opcode0(58); // iM - if (*ss1=='a') _cimg_mp_opcode0(59); // ia - if (*ss1=='v') _cimg_mp_opcode0(60); // iv - } - if (*ss1=='m') { - if (*ss=='x') _cimg_mp_opcode0(61); // xm - if (*ss=='y') _cimg_mp_opcode0(62); // ym - if (*ss=='z') _cimg_mp_opcode0(63); // zm - if (*ss=='c') _cimg_mp_opcode0(64); // cm - } - if (*ss1=='M') { - if (*ss=='x') _cimg_mp_opcode0(65); // xM - if (*ss=='y') _cimg_mp_opcode0(66); // yM - if (*ss=='z') _cimg_mp_opcode0(67); // zM - if (*ss=='c') _cimg_mp_opcode0(68); // cM - } - } - if (ss3==se) { - if (*ss=='x' && *ss1=='/' && *ss2=='w') _cimg_mp_opcode0(3); - if (*ss=='y' && *ss1=='/' && *ss2=='h') _cimg_mp_opcode0(4); - if (*ss=='z' && *ss1=='/' && *ss2=='d') _cimg_mp_opcode0(5); - if (*ss=='c' && *ss1=='/' && *ss2=='s') _cimg_mp_opcode0(6); - } - - // Look for variable declarations. - for (char *s = se2; s>ss; --s) if (*s==';' && level[s-expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s+1,se)); } - for (char *s = ss1, *ps = ss, *ns = ss2; s variable_name(ss,s-ss+1); variable_name.back() = 0; - bool is_valid_name = true; - if ((*ss>='0' && *ss<='9') || - (s==ss+1 && (*ss=='x' || *ss=='y' || *ss=='z' || *ss=='c' || - *ss=='w' || *ss=='h' || *ss=='d' || *ss=='s' || - *ss=='e' || *ss=='u' || *ss=='g' || *ss=='i')) || - (s==ss+2 && ((*ss=='p' && *(ss+1)=='i') || - (*ss=='i' && (*(ss+1)=='m' || *(ss+1)=='M' || *(ss+1)=='a' || *(ss+1)=='v'))))) is_valid_name = false; - for (const char *ns = ss; ns'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') { - is_valid_name = false; break; - } - if (!is_valid_name) { - *se = saved_char; - if (!std::strcmp(variable_name,"x") || !std::strcmp(variable_name,"y") || !std::strcmp(variable_name,"z") || - !std::strcmp(variable_name,"c") || !std::strcmp(variable_name,"w") || !std::strcmp(variable_name,"h") || - !std::strcmp(variable_name,"d") || !std::strcmp(variable_name,"s") || !std::strcmp(variable_name,"e") || - !std::strcmp(variable_name,"u") || !std::strcmp(variable_name,"g") || !std::strcmp(variable_name,"i") || - !std::strcmp(variable_name,"pi") || !std::strcmp(variable_name,"im") || !std::strcmp(variable_name,"iM") || - !std::strcmp(variable_name,"ia") || !std::strcmp(variable_name,"iv")) - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s() : Invalid assignment of reserved variable name '%s' in specified expression '%s'.", - pixel_type(),calling_function, - variable_name._data,expr._data); - else - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s() : Invalid variable name '%s' in specified expression '%s'.", - pixel_type(),calling_function, - variable_name._data,expr._data); - } - for (unsigned int i = 0; i::%s() : Invalid multiple assignments of variable '%s' in specified expression '%s'.", - pixel_type(),calling_function, - variable_name._data,expr._data); - } - const unsigned int src_pos = compile(s+1,se); - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int dest_pos = mempos++; - variable_name.move_to(label[dest_pos]); - CImg::vector(7,dest_pos,src_pos).move_to(code); - _cimg_mp_return(dest_pos); - } - - // Look for unary/binary operators. The operator precedences is defined as in C++. - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(8,compile(ss,s),compile(s+2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(9,compile(ss,s),compile(s+2,se)); - for (char *s = se2; s>ss; --s) if (*s=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(10,compile(ss,s),compile(s+1,se)); - for (char *s = se2; s>ss; --s) if (*s=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(11,compile(ss,s),compile(s+1,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='!' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(12,compile(ss,s),compile(s+2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='=' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(13,compile(ss,s),compile(s+2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(14,compile(ss,s),compile(s+2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(15,compile(ss,s),compile(s+2,se)); - for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps) - if (*s=='<' && *ns!='<' && *ps!='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(16,compile(ss,s),compile(s+1,se)); - for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps) - if (*s=='>' && *ns!='>' && *ps!='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(17,compile(ss,s),compile(s+1,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(18,compile(ss,s),compile(s+2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(19,compile(ss,s),compile(s+2,se)); - for (char *s = se2, *ps = se3; s>ss; --s, --ps) - if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && - (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel) - _cimg_mp_opcode2(21,compile(ss,s),compile(s+1,se)); - for (char *s = se2, *ps = se3; s>ss; --s, --ps) - if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && - (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel) - _cimg_mp_opcode2(20,compile(ss,s),compile(s+1,se)); - for (char *s = se2; s>ss; --s) if (*s=='*' && level[s-expr._data]==clevel) _cimg_mp_opcode2(22,compile(ss,s),compile(s+1,se)); - for (char *s = se2; s>ss; --s) if (*s=='/' && level[s-expr._data]==clevel) _cimg_mp_opcode2(23,compile(ss,s),compile(s+1,se)); - for (char *s = se2, *ns = se1; s>ss; --s, --ns) - if (*s=='%' && *ns!='^' && level[s-expr._data]==clevel) - _cimg_mp_opcode2(24,compile(ss,s),compile(s+1,se)); - if (ssss; --s) if (*s=='^' && level[s-expr._data]==clevel) _cimg_mp_opcode2(25,compile(ss,s),compile(s+1,se)); - - // Look for a function call or a parenthesis. - if (*se1==')') { - if (*ss=='(') _cimg_mp_return(compile(ss1,se1)); - if (!std::strncmp(ss,"sin(",4)) _cimg_mp_opcode1(29,compile(ss4,se1)); - if (!std::strncmp(ss,"cos(",4)) _cimg_mp_opcode1(30,compile(ss4,se1)); - if (!std::strncmp(ss,"tan(",4)) _cimg_mp_opcode1(31,compile(ss4,se1)); - if (!std::strncmp(ss,"asin(",5)) _cimg_mp_opcode1(32,compile(ss5,se1)); - if (!std::strncmp(ss,"acos(",5)) _cimg_mp_opcode1(33,compile(ss5,se1)); - if (!std::strncmp(ss,"atan(",5)) _cimg_mp_opcode1(34,compile(ss5,se1)); - if (!std::strncmp(ss,"sinh(",5)) _cimg_mp_opcode1(35,compile(ss5,se1)); - if (!std::strncmp(ss,"cosh(",5)) _cimg_mp_opcode1(36,compile(ss5,se1)); - if (!std::strncmp(ss,"tanh(",5)) _cimg_mp_opcode1(37,compile(ss5,se1)); - if (!std::strncmp(ss,"log10(",6)) _cimg_mp_opcode1(38,compile(ss6,se1)); - if (!std::strncmp(ss,"log(",4)) _cimg_mp_opcode1(39,compile(ss4,se1)); - if (!std::strncmp(ss,"exp(",4)) _cimg_mp_opcode1(40,compile(ss4,se1)); - if (!std::strncmp(ss,"sqrt(",5)) _cimg_mp_opcode1(41,compile(ss5,se1)); - if (!std::strncmp(ss,"sign(",5)) _cimg_mp_opcode1(42,compile(ss5,se1)); - if (!std::strncmp(ss,"abs(",4)) _cimg_mp_opcode1(43,compile(ss4,se1)); - if (!std::strncmp(ss,"atan2(",6)) { - char *s1 = ss6; while (s1='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; } - _cimg_mp_opcode3(46,value,round,direction); } - if (!std::strncmp(ss,"?(",2) || !std::strncmp(ss,"u(",2)) { - if (*ss2==')') _cimg_mp_opcode2(0,0,1); - char *s1 = ss2; while (s1='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::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::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg1,_cimg_mp_vector_size(arg2)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg1,_cimg_mp_vector_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value + is_parallelizable = false; + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + } else if (s1='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::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::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg5,p1,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg5,p1,arg1,arg2,arg3,_cimg_mp_vector_size(arg5)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg5); + if (*ss>='i') + CImg::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::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg5,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg5,arg1,arg2,arg3,_cimg_mp_vector_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; nsss) { + variable_name[s0 - ss] = 0; // Remove brackets in variable name + arg1 = ~0U; // Vector slot + arg2 = compile(++s0,ve1,depth1,0); // Index + arg3 = compile(s + 1,se,depth1,0); // 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); // Variable does not exist -> error + else { // Variable already exists + if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0); // 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_vector_size(arg1)) { + arg1+=nb + 1; + CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); + _cimg_mp_return(arg1); + } + compile(ss,s,depth1,0); // 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::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_vector_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; nsss) { // 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(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0); + ++s; while (*s && (signed char)*s<=' ') ++s; + CImg(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_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 && (signed char)*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' '; ++ns) + if (!is_varchar(*ns)) { is_sth = false; break; } + s3 = ns; // End of the argument name + while (*ns && (signed char)*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_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 + p2macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign + *(ps - 1) = (char)p1; + if (ps + p26 && !std::strncmp(variable_name,"const ",6); + + s0 = variable_name._data; + if (is_const) { + s0+=6; while ((signed char)*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); + 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_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_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1)) { // Vector + if (_cimg_mp_is_vector(arg2)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_vector_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_vector_size(arg1),arg2). + move_to(code); + } else // Scalar + CImg::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); // Lvalue slot + arg2 = compile(s + 1,se,depth1,0); // 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::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_vector_size(arg3),arg4,arg2). + move_to(code); + _cimg_mp_return(arg2); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar + 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::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::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 + 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::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::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 + is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg3,_cimg_mp_vector_size(arg2)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg3,_cimg_mp_vector_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value + is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg2,p1,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg2,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg2)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg2,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg2,arg3,arg4,arg5,_cimg_mp_vector_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_vector_size(arg1)); + if (_cimg_mp_is_vector(arg2)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_vector_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_vector_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::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_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); // Vector slot + arg2 = compile(s + 1,se,depth1,0); // 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::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code); + else if (*ps=='/') + CImg::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code); + else + CImg::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::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 + 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::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + } + + } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value + 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::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_vector_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); // Variable slot + arg2 = compile(s + 1,se,depth1,0); // 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::vector((ulongT)op,arg1,arg2).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_vector_size(arg3),arg4,arg1). + move_to(code); + _cimg_mp_return(arg1); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar + 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::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::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::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 + 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::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::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::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 + is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value + is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_vector_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_vector_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::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_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::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); + _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); + _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::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); + _cimg_mp_check_type(arg1,1,1,0); + if (!arg1) _cimg_mp_return(0); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0); + _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::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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 2,se,depth1,0); + if (arg1==arg2) _cimg_mp_return(0); + p1 = _cimg_mp_vector_size(arg1); + p2 = _cimg_mp_vector_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); + arg2 = compile(s + 2,se,depth1,0); + if (arg1==arg2) _cimg_mp_return(1); + p1 = _cimg_mp_vector_size(arg1); + p2 = _cimg_mp_vector_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); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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]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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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 &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::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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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 &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::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); + arg2 = compile(s + 2,se,depth1,0); + _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::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); + arg2 = compile(s + 2,se,depth1,0); + _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::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::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); + arg2 = compile(s + 1,se,depth1,0); + p2 = _cimg_mp_vector_size(arg2); + if (p2>0 && _cimg_mp_vector_size(arg1)==p2*p2) { // Particular case of matrix multiplication + pos = vector(p2); + CImg::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_vector_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 &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::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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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='0' && *ss2<='9'))) { // Unary plus ('+') + _cimg_mp_op("Operator '+'"); + _cimg_mp_return(compile(ss1,se,depth1,0)); + } + + if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') + _cimg_mp_op("Operator '-'"); + arg1 = compile(ss1,se,depth1,0); + 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); + 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); + 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); + 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); + arg2 = compile(s + 2,se,depth1,0); + _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::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::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::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); + } + CImg::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); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_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); + 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 = ss1ss && (*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):compile(ss,se2,depth1,ref); // 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::vector((ulongT)op,arg1,1).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_vector_size(arg3),arg4,arg1). + move_to(code); + _cimg_mp_return(pos); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ + 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::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::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::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)++ + 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::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::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::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]++ + 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::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ + 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::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_vector_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::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_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,'['); do { --s1; } while ((signed char)*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::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::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 (s0ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss) { // Vector value + arg1 = compile(ss,s0,depth1,0); + 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_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 sub-vector extraction + p1 = _cimg_mp_vector_size(arg1); + arg2 = compile(++s0,s1,depth1,0); // Starting indice + arg3 = compile(++s1,se1,depth1,0); // Length + _cimg_mp_check_constant(arg3,2,3); + arg3 = (unsigned int)mem[arg3]; + pos = vector(arg3); + CImg::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); + if (_cimg_mp_is_constant(arg2)) { // Constant index + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_vector_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_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_vector_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_vector_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)); // Simple parentheses + _cimg_mp_op("Value accessor '()'"); + is_relative = *ss=='j' || *ss=='J'; + s0 = s1 = std::strchr(ss,'('); do { --s1; } while ((signed char)*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 (s01) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + if (s1 opcode; - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(ss[1]=='i'?48:49,pos).move_to(opcode); - for (char *s = ss4; s::vector(compile(s,ns)).move_to(opcode); - s = ns; + 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; + } + _cimg_mp_check_vector0(p2); + pos = vector(p2); + if (p1!=~0U) + CImg::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::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); } - (opcode>'y').move_to(code); _cimg_mp_return(pos); } - if (!std::strncmp(ss,"arg(",4)) { - CImgList opcode; - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(69,pos).move_to(opcode); - for (char *s = ss4; s::vector(compile(s,ns)).move_to(opcode); - s = ns; - } - (opcode>'y').move_to(code); - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"narg(",5)) { - if (*ss5==')') _cimg_mp_return(0); - unsigned int nb_args = 0; - for (char *s = ss5; s=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = nb_args; - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"isval(",6)) { - char sep = 0, end = 0; double val = 0; - if (std::sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); - _cimg_mp_return(0); - } - if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(50,compile(ss6,se1)); - if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(51,compile(ss6,se1)); - if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(52,compile(ss6,se1)); - if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(53,compile(ss7,se1)); - if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { - unsigned int value = 0, nb = 1; - char *s1 = ss4; while (s11) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + if (s1::vector((ulongT)mp_arg,0,0,p2,arg1,arg2).move_to(_opcode); + for (s = ++s2; s::vector(arg3).move_to(_opcode); + ++p3; + s = ns; + } + (_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::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return(_cimg_mp_slot_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::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return(_cimg_mp_slot_nan); + } + } + break; + + case 'c' : + if ((*ss1=='m' || *ss1=='M') && *ss2=='(') { // cm(), cM() + _cimg_mp_op(*ss1=='m'?"Function 'cm()'":"Function 'cM()'"); + if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)(*ss1=='m'?mp_image_cm:mp_image_cM),pos,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value + _cimg_mp_op("Function 'cabs()'"); + arg1 = compile(ss5,se1,depth1,0); + _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); + _cimg_mp_check_type(arg1,0,2,2); + _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1); + } + + if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root + _cimg_mp_op("Function 'cbrt()'"); + arg1 = compile(ss5,se1,depth1,0); + 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); + _cimg_mp_check_type(arg1,0,2,2); + pos = vector(2); + CImg::vector((ulongT)mp_complex_conj,pos,arg1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential + _cimg_mp_op("Function 'cexp()'"); + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_type(arg1,0,2,2); + pos = vector(2); + CImg::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); + _cimg_mp_check_type(arg1,0,2,2); + pos = vector(2); + CImg::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::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return(_cimg_mp_slot_nan); + } + } + + if (!std::strncmp(ss,"copy(",5)) { // Memory copy + _cimg_mp_op("Function 'copy()'"); + ref.assign(14); + s1 = ss5; while (s1(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); + 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); + 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,"crop(",5)) { // Image crop + _cimg_mp_op("Function 'crop()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s0::sequence(_cimg_mp_vector_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_vector_size(arg1)); + opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(_opcode); + is_sth = true; + } else { + _cimg_mp_check_type(arg1,pos + 1,1,0); + CImg::vector(arg1).move_to(_opcode); + } + s = ns; + } + (_opcode>'y').move_to(opcode); + + arg1 = 0; arg2 = (p1!=~0U); + switch (opcode._height) { + case 0 : case 1 : + CImg::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); + break; + case 2 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); + arg1 = arg2?3:2; + break; + case 3 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); + arg1 = arg2?3:2; + break; + case 4 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary). + move_to(opcode); + arg1 = (is_sth?2:1) + arg2; + break; + case 5 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). + move_to(opcode); + arg1 = (is_sth?2:1) + arg2; + break; + case 6 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + _cimg_mp_boundary).move_to(opcode); + arg1 = (is_sth?2:4) + arg2; + break; + case 7 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + opcode[6]).move_to(opcode); + arg1 = (is_sth?2:4) + arg2; + break; + case 8 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], + opcode[7],_cimg_mp_boundary).move_to(opcode); + arg1 = (is_sth?2:5) + arg2; + break; + case 9 : + arg1 = (is_sth?2:5) + arg2; + 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_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 &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_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::vector((ulongT)mp_image_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::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 (s1val2?val2:val); + } + _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); + } + break; + + case 'd' : + if (*ss1=='(') { // Image depth + _cimg_mp_op("Function 'd()'"); + if (*ss2=='#') { p1 = compile(ss3,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss2!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_d,pos,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"date(",5)) { // Date and file date + _cimg_mp_op("Function 'date()'"); + s1 = ss5; while (s1::string(ss6,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + ((CImg::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 + _cimg_mp_op("Function 'display()'"); + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return(_cimg_mp_slot_nan); + } + s1 = ss8; while (s1::string(ss8,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(arg1)) + ((CImg::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_vector_size(arg1)), + variable_name)>'y').move_to(opcode); + else + ((CImg::vector((ulongT)mp_print,arg1,0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + + ((CImg::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_vector_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); + } + + if (!std::strncmp(ss,"det(",4)) { // Matrix determinant + _cimg_mp_op("Function 'det()'"); + arg1 = compile(ss4,se1,depth1,0); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + _cimg_mp_scalar2(mp_det,arg1,p1); + } + + if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix + _cimg_mp_op("Function 'diag()'"); + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_type(arg1,1,2,0); + p1 = _cimg_mp_vector_size(arg1); + pos = vector(p1*p1); + CImg::vector((ulongT)mp_diag,pos,arg1,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"dot(",4)) { // Dot product + _cimg_mp_op("Function 'dot()'"); + s1 = ss4; while (s1::vector((ulongT)mp_dowhile,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_vector_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 + is_parallelizable = false; + _cimg_mp_op("Function 'draw()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s01) { + arg3 = arg2 + 1; + if (p2>2) { + arg4 = arg3 + 1; + if (p2>3) arg5 = arg4 + 1; + } + } + ++s0; + is_sth = true; + } else { + if (s0::vector((ulongT)mp_image_draw,arg1,(ulongT)_cimg_mp_vector_size(arg1),p1,arg2,arg3,arg4,arg5, + 0,0,0,0,1,(ulongT)~0U,0,1).move_to(opcode); + + arg2 = arg3 = arg4 = arg5 = ~0U; + p2 = p1!=~0U?0:1; + if (s0::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); + arg1 = compile(ss4,se1,depth1,p_ref); + code.swap(code_end); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"exp(",4)) { // Exponential + _cimg_mp_op("Function 'exp()'"); + arg1 = compile(ss4,se1,depth1,0); + 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); + _cimg_mp_check_constant(arg1,1,3); + p1 = (unsigned int)mem[arg1]; + pos = vector(p1*p1); + CImg::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); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial(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); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci(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::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_vector_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); + } + break; + + case 'g' : + if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function + _cimg_mp_op("Function 'gauss()'"); + s1 = ss6; while (s1::vector((ulongT)mp_image_h,pos,p1).move_to(code); + _cimg_mp_return(pos); + } + + case 'i' : + if ((*ss1=='m' || *ss1=='M' || *ss1=='a' || *ss1=='v' || *ss1=='s' || *ss1=='p' || *ss1=='c') && + *ss2=='(') { // im(), iM(), ia(), iv(), is(), ip(), ic() + _cimg_mp_op(*ss1=='m'?"Function 'im()'": + *ss1=='M'?"Function 'iM()'": + *ss1=='a'?"Function 'ia()'": + *ss1=='v'?"Function 'iv()'": + *ss1=='s'?"Function 'is()'": + *ss1=='p'?"Function 'ip()'": + "Function 'ic()"); + if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)(*ss1=='m'?mp_image_im: + *ss1=='M'?mp_image_iM: + *ss1=='a'?mp_image_ia: + *ss1=='v'?mp_image_iv: + *ss1=='s'?mp_image_is: + *ss1=='p'?mp_image_ip: + mp_image_ic),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::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,"init(",5)) { // Init + _cimg_mp_op("Function 'init()'"); + code.swap(code_init); + arg1 = compile(ss5,se1,depth1,p_ref); + code.swap(code_init); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"int(",4)) { // Integer cast + _cimg_mp_op("Function 'int()'"); + arg1 = compile(ss4,se1,depth1,0); + 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); + if (_cimg_mp_is_vector(arg1)) { + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + pos = vector(p1*p1); + CImg::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); + 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.0 || mem[arg1]==1.0); + _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::vector((ulongT)mp_isin,pos,0).move_to(_opcode); + for (s = ss5; s::sequence(_cimg_mp_vector_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_vector_size(arg1)). + move_to(_opcode); + else CImg::vector(arg1).move_to(_opcode); + s = ns; + } + (_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); + 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::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); + 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)==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); + 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::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 (!std::strncmp(ss,"log(",4)) { // Natural logarithm + _cimg_mp_op("Function 'log()'"); + arg1 = compile(ss4,se1,depth1,0); + 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); + 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); + 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); + 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 (s1expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_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::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::vector((ulongT)mp_norm0,pos,0).move_to(_opcode); break; + case 1 : + CImg::vector((ulongT)mp_norm1,pos,0).move_to(_opcode); break; + case 2 : + CImg::vector((ulongT)mp_norm2,pos,0).move_to(_opcode); break; + case ~0U : + CImg::vector((ulongT)mp_norminf,pos,0).move_to(_opcode); break; + default : + CImg::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). + move_to(_opcode); + } + for ( ; s::sequence(_cimg_mp_vector_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_vector_size(arg2)). + move_to(_opcode); + else CImg::vector(arg2).move_to(_opcode); + s = ns; + } + + (_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 (s1expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_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::vector((ulongT)mp_matrix_pseudoinv,pos,arg1,p2,p3).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"print(",6)) { // Print expressions + _cimg_mp_op("Function 'print()'"); + for (s = ss6; s::string(s,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(pos)) // Vector + ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_vector_size(pos)), + variable_name)>'y').move_to(opcode); + else // Scalar + ((CImg::vector((ulongT)mp_print,pos,0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *ns = c1; s = ns; + } + _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::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_vector_size(arg1), + arg3,arg4).move_to(code); + _cimg_mp_return(pos); + + } else { // Image + is_parallelizable = false; + s0 = ss8; while (s0::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0). + move_to(opcode); + pos = 0; + for (s = s0; s10) { + *se = saved_char; + s0 = ss - 4>expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_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()?"...":""); + } + opcode.move_to(code); + _cimg_mp_return(_cimg_mp_slot_nan); + } + } + + if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse + _cimg_mp_op("Function 'reverse()'"); + arg1 = compile(ss8,se1,depth1,0); + if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1); + p1 = _cimg_mp_vector_size(arg1); + pos = vector(p1); + CImg::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 (s11) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + arg4 = compile(++s1,se1,depth1,0); + } else { + s2 = s1 + 1; while (s2::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::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::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::vector((ulongT)mp_single,arg1,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine + _cimg_mp_op("Function 'sinh()'"); + arg1 = compile(ss5,se1,depth1,0); + 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); + _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_vector_size(arg1)); + } + + if (!std::strncmp(ss,"solve(",6)) { // Solve linear system + _cimg_mp_op("Function 'solve()'"); + s1 = ss6; while (s1expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_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::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()'"); + s1 = ss6; while (s1expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_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::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sqr(",4)) { // Square + _cimg_mp_op("Function 'sqr()'"); + arg1 = compile(ss4,se1,depth1,0); + 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); + 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,"stod(",5)) { // String to double + _cimg_mp_op("Function 'stod()'"); + s1 = ss5; while (s1expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_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::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); + 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); + 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); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_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 (s1expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_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::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 (s1ss6 && *s0==',') ++s0; + s1 = s0; while (s1s0) { + *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 (arg20) || + !std::strncmp(ss,"vector(",7) || + (!std::strncmp(ss,"vector",6) && ss7::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(_opcode); + arg2+=arg4; + } else { CImg::vector(arg3).move_to(_opcode); ++arg2; } + s = ns; + } + if (arg1==~0U) arg1 = arg2; + _cimg_mp_check_vector0(arg1); + pos = vector(arg1); + _opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return(pos); + } + break; + + case 'w' : + if (*ss1=='(') { // Image width + _cimg_mp_op("Function 'w()'"); + if (*ss2=='#') { p1 = compile(ss3,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss2!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::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=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::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=='#') { p1 = compile(ss5,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss4!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::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=='#') { p1 = compile(ss6,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss5!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_whds,pos,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"while(",6) || !std::strncmp(ss,"whiledo(",8)) { // While...do + _cimg_mp_op("Function 'whiledo()'"); + s0 = *ss5=='('?ss6:ss8; + s1 = s0; while (s1::vector((ulongT)mp_whiledo,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 ((*ss1=='m' || *ss1=='M') && *ss2=='(') { // xm(), xM() + _cimg_mp_op(*ss1=='m'?"Function 'xm()'":"Function 'xM()'"); + if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)(*ss1=='m'?mp_image_xm:mp_image_xM),pos,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"xor(",4)) { // Xor + _cimg_mp_op("Function 'xor()'"); + s1 = ss4; while (s1::vector((ulongT)(*ss1=='m'?mp_image_ym:mp_image_yM),pos,p1).move_to(code); + _cimg_mp_return(pos); + } + break; + + case 'z' : + if ((*ss1=='m' || *ss1=='M') && *ss2=='(') { // zm(), zM() + _cimg_mp_op(*ss1=='m'?"Function 'zm()'":"Function 'zM()'"); + if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified + else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)(*ss1=='m'?mp_image_zm:mp_image_zM),pos,p1).move_to(code); + _cimg_mp_return(pos); + } + 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,"std(",4) || !std::strncmp(ss,"variance(",9) || + !std::strncmp(ss,"prod(",5) || !std::strncmp(ss,"mean(",5) || + !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7)) { // Multi-argument functions + _cimg_mp_op(*ss=='a'?(ss[3]=='('?"Function 'arg()'":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 'variance()'": + ss[1]=='i'?"Function 'min()'": + ss[1]=='a'?"Function 'max()'": + ss[2]=='a'?"Function 'mean()'":"Function 'med()'"); + op = *ss=='a'?(ss[3]=='('?mp_arg: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_variance: + ss[1]=='i'?mp_min: + ss[1]=='a'?mp_max: + ss[2]=='a'?mp_mean: + mp_median; + is_sth = true; // Tell if all arguments are constant + pos = scalar(); + CImg::vector((ulongT)op,pos,0).move_to(_opcode); + for (s = std::strchr(ss,'(') + 1; s::sequence(_cimg_mp_vector_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_vector_size(arg2)). + move_to(_opcode); + else CImg::vector(arg2).move_to(_opcode); + is_sth&=_cimg_mp_is_constant(arg2); + s = ns; + } + (_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 && (signed char)*s<=' ') ++s; + if (*s==')' && !p1) break; + ns = s; while (ns _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 && (signed char)*s<=' ') ++s; + if (*s==')' && p1==1) break; // Function has no arguments + if (p1>p2) { ++p1; break; } + ns = s; while (ns _pexpr(_expr._width); + ns = _pexpr._data; + for (ps = _expr._data, c1 = ' '; *ps; ++ps) { + if ((signed char)*ps>' ') c1 = *ps; + *(ns++) = c1; + } + *ns = 0; + + CImg _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); + 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 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_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_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(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_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::vector((ulongT)mp_string_init,pos,arg1).move_to(_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(_opcode); + std::memcpy((char*)_opcode[1]._data,variable_name,arg1); + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); } + // Vector initializer [ ... ]. + if (*ss=='[' && *se1==']') { + _cimg_mp_op("Vector initializer"); + s1 = ss1; while (s1s1 && (signed char)*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(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::vector((ulongT)mp_string_init,pos,arg1).move_to(_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(_opcode); + std::memcpy((char*)_opcode[1]._data,variable_name,arg1); + (_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::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(_opcode); + arg1+=arg3; + } else { CImg::vector(arg2).move_to(_opcode); ++arg1; } + s = ns; + } + _cimg_mp_check_vector0(arg1); + pos = vector(arg1); + _opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (_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::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::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 variable_name(ss,se-ss+1); variable_name.back() = 0; - for (unsigned int i = 0; i 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_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_math_parser] " - "CImg<%s>::%s() : Invalid item '%s' in specified expression '%s'.\n", - pixel_type(),calling_function, - variable_name._data,expr._data); - return 0; + "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_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + return *result; + } + + // Evaluation procedure (return output values in vector 'output'). + template + 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_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_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + + // Return type of a memory element as a string. + CImg s_type(const unsigned int arg) const { + CImg res; + if (_cimg_mp_is_vector(arg)) { // Vector + CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); + std::sprintf(res._data + 6,"%u",_cimg_mp_vector_size(arg)); + } else CImg::string("scalar").move_to(res); + return res; + } + + // Insert constant value in memory. + unsigned int constant(const double val) { + + // Search for built-in constant. + 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; + if (cimg::type::is_nan(val)) return _cimg_mp_slot_nan; + + // 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=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::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>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:scalar(); + CImg::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>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:scalar(); + CImg::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>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:scalar(); + CImg::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>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:scalar(); + CImg::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>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:scalar(); + CImg::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>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:scalar(); + CImg::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>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:scalar(); + CImg::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 calling_function_s() const { + CImg 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_vector_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_vector_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::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::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_vector_size(pos); + if (siz>24) CImg::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::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_vector_size(pos); + if (siz>24) CImg::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::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_vector_size(arg1), + pos = is_comp_vector(arg1)?arg1:vector(siz); + if (siz>24) CImg::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::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_vector_size(arg1), + pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz); + if (siz>24) CImg::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::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_vector_size(arg1), + pos = is_comp_vector(arg1)?arg1:vector(siz); + if (siz>24) CImg::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::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_vector_size(arg2), + pos = is_comp_vector(arg2)?arg2:vector(siz); + if (siz>24) CImg::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::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_vector_size(arg1), + pos = is_comp_vector(arg1)?arg1:vector(siz); + if (siz>24) CImg::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::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_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_vector_size(arg), + n = (unsigned int)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_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_vector_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 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_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_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()?"...":""); + } + } + + // Check a vector is not 0-dimensional, or with unknown dimension at compile time. + void check_vector0(const unsigned int dim, + char *const ss, char *const se, const char saved_char) { + char *s0 = 0; + if (!dim) { + *se = saved_char; + s0 = ss - 4>expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s%s Invalid construction of a 0-dimensional vector, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else if (dim==~0U) { + *se = saved_char; + s0 = ss - 4>expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s%s Invalid construction of a vector with dynamic size, " + "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. - double mp_u() { - return mem[opcode(2)] + cimg::rand()*(mem[opcode(3)]-mem[opcode(2)]); + // 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)); } - double mp_g() { + + 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_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::nan(); + } + if (ind>=nb_args) return 0; + return _mp_arg(ind + 4); + } + + 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; ival) { 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_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::nan(); + } + + static double mp_breakpoint(_cimg_math_parser& mp) { + cimg_abort_test(); + cimg::unused(mp); + return cimg::type::nan(); + } + + static double mp_cbrt(_cimg_math_parser& mp) { + return cimg::cbrt(_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::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::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::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::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::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::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::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::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::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::nan(); + } + + static double mp_continue(_cimg_math_parser& mp) { + mp.break_type = 2; + mp.p_code = mp.p_break - 1; + return cimg::type::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_cross(_cimg_math_parser& mp) { + CImg + 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::nan(); + } + + static double mp_cut(_cimg_math_parser& mp) { + double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4); + return valcmax?cmax:val; + } + + static double mp_debug(_cimg_math_parser& mp) { + CImg 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) + { + std::fprintf(cimg::output(), + "\n[_cimg_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 *const p_end = (++mp.p_code) + mp.opcode[3]; + CImg _op; + for ( ; mp.p_code &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 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_indent-=3; + std::fprintf(cimg::output(), + "\n[_cimg_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(ptrs,k,k,1,1,true).det(); + } + + static double mp_diag(_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]; + CImg(ptrd,k,k,1,1,true) = CImg(ptrs,1,k,1,1,true).get_diagonal(); + return cimg::type::nan(); + } + + static double mp_display_memory(_cimg_math_parser& mp) { + cimg::unused(mp); + std::fputc('\n',cimg::output()); + mp.mem.display("[_cimg_math_parser] Memory snapshot"); + return cimg::type::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 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 expr(mp.opcode[2] - 8); + const ulongT *ptrs = mp.opcode._data + 8; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + ((CImg::string("[_cimg_math_parser] ",false,true),expr)>'x').move_to(expr); + cimg::strellipsize(expr); + std::fputc('\n',cimg::output()); + img.display(expr._data); + return cimg::type::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(&_mp_arg(2) + 1,1,siz,1,1,true). + dot(CImg(&_mp_arg(3) + 1,1,siz,1,1,true)); + } + + static double mp_dowhile(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *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(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::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_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_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_eq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)==_mp_arg(3)); + } + + 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(ptrd,k,k,1,1,true).identity_matrix(); + return cimg::type::nan(); + } + + static double mp_factorial(_cimg_math_parser& mp) { + return cimg::factorial(_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=ptrb && *ptr!=val) --ptr; + return ptr=(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=ptr1b && *ptr1!=*ptr2b) --ptr1; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b); + return p2 + *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(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::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_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_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_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_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_g(_cimg_math_parser& mp) { + cimg::unused(mp); return cimg::grand(); } - double mp_i() { - return (double)reference.atXYZC((int)mem[8],(int)mem[9],(int)mem[10],(int)mem[11],0); - } - double mp_xw() { - return mem[8]/reference.width(); - } - double mp_yh() { - return mem[9]/reference.height(); - } - double mp_zd() { - return mem[10]/reference.depth(); - } - double mp_cs() { - return mem[11]/reference.spectrum(); - } - double mp_equal() { - return mem[opcode[2]]; - } - double mp_logical_and() { - return (double)((bool)mem[opcode(2)] && (bool)mem[opcode(3)]); - } - double mp_logical_or() { - return (double)((bool)mem[opcode(2)] || (bool)mem[opcode(3)]); - } - double mp_infeq() { - return (double)(mem[opcode(2)]<=mem[opcode(3)]); - } - double mp_supeq() { - return (double)(mem[opcode(2)]>=mem[opcode(3)]); - } - double mp_noteq() { - return (double)(mem[opcode(2)]!=mem[opcode(3)]); - } - double mp_eqeq() { - return (double)(mem[opcode(2)]==mem[opcode(3)]); - } - double mp_inf() { - return (double)(mem[opcode(2)]mem[opcode(3)]); - } - double mp_add() { - return mem[opcode(2)] + mem[opcode(3)]; - } - double mp_sub() { - return mem[opcode(2)] - mem[opcode(3)]; - } - double mp_mul() { - return mem[opcode(2)] * mem[opcode(3)]; - } - double mp_div() { - return mem[opcode(2)] / mem[opcode(3)]; - } - double mp_minus() { - return -mem[opcode(2)]; - } - double mp_not() { - return !mem[opcode(2)]; - } - double mp_logical_not() { - return !mem[opcode(2)]; - } - double mp_bitwise_not() { - return ~(unsigned long)mem[opcode(2)]; - } - double mp_modulo() { - return cimg::mod(mem[opcode(2)],mem[opcode(3)]); - } - double mp_bitwise_and() { - return ((unsigned long)mem[opcode(2)] & (unsigned long)mem[opcode(3)]); - } - double mp_bitwise_or() { - return ((unsigned long)mem[opcode(2)] | (unsigned long)mem[opcode(3)]); - } - double mp_pow() { - return std::pow(mem[opcode(2)],mem[opcode(3)]); - } - double mp_sin() { - return std::sin(mem[opcode(2)]); - } - double mp_cos() { - return std::cos(mem[opcode(2)]); - } - double mp_tan() { - return std::tan(mem[opcode(2)]); - } - double mp_asin() { - return std::asin(mem[opcode(2)]); - } - double mp_acos() { - return std::acos(mem[opcode(2)]); - } - double mp_atan() { - return std::atan(mem[opcode(2)]); - } - double mp_sinh() { - return std::sinh(mem[opcode(2)]); - } - double mp_cosh() { - return std::cosh(mem[opcode(2)]); - } - double mp_tanh() { - return std::tanh(mem[opcode(2)]); - } - double mp_log10() { - return std::log10(mem[opcode(2)]); - } - double mp_log() { - return std::log(mem[opcode(2)]); - } - double mp_exp() { - return std::exp(mem[opcode(2)]); - } - double mp_sqrt() { - return std::sqrt(mem[opcode(2)]); - } - double mp_sign() { - return cimg::sign(mem[opcode(2)]); - } - double mp_abs() { - return cimg::abs(mem[opcode(2)]); - } - double mp_atan2() { - return std::atan2(mem[opcode(2)],mem[opcode(3)]); - } - double mp_if() { - return mem[opcode(2)]?mem[opcode(3)]:mem[opcode(4)]; - } - double mp_round() { - return cimg::round(mem[opcode(2)],mem[opcode(3)],(int)mem[opcode(4)]); - } - double mp_ixyzc() { - const int b = (int)mem[opcode(6)]; - if (b==2) return (double)reference.atXYZC(cimg::mod((int)mem[opcode(2)],reference.width()), - cimg::mod((int)mem[opcode(3)],reference.height()), - cimg::mod((int)mem[opcode(4)],reference.depth()), - cimg::mod((int)mem[opcode(5)],reference.spectrum())); - if (b==1) return (double)reference.atXYZC((int)mem[opcode(2)], - (int)mem[opcode(3)], - (int)mem[opcode(4)], - (int)mem[opcode(5)]); - return (double)reference.atXYZC((int)mem[opcode(2)], - (int)mem[opcode(3)], - (int)mem[opcode(4)], - (int)mem[opcode(5)],0); - } - double mp_min() { - double val = mem[opcode(2)]; - for (unsigned int i = 3; i>(unsigned int)mem[opcode(3)]; - } - double mp_sinc() { - return cimg::sinc(mem[opcode(2)]); - } - double mp_im() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[0]:0; - } - double mp_iM() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[1]:0; - } - double mp_ia() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[2]:0; - } - double mp_iv() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[3]:0; - } - double mp_xm() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[4]:0; - } - double mp_ym() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[5]:0; - } - double mp_zm() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[6]:0; - } - double mp_cm() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[7]:0; - } - double mp_xM() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[8]:0; - } - double mp_yM() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[9]:0; - } - double mp_zM() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[10]:0; - } - double mp_cM() { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - return reference_stats?reference_stats[11]:0; - } - double mp_arg() { - const int _ind = (int)mem[opcode(2)]; - const unsigned int nb_args = opcode._height-2, ind = _ind<0?_ind+nb_args:(unsigned int)_ind; - if (ind>=nb_args) return 0; - return mem[opcode(ind+2)]; - } - double mp_int() { - return (double)(long)mem[opcode(2)]; + + 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))/std::sqrt(2*s*s*cimg::PI); } - // Evaluation procedure, with image data. - double eval(const double x, const double y, const double z, const double c) { - typedef double (_cimg_math_parser::*mp_func)(); - const mp_func mp_funcs[] = { - &_cimg_math_parser::mp_u, // 0 - &_cimg_math_parser::mp_g, // 1 - &_cimg_math_parser::mp_i, // 2 - &_cimg_math_parser::mp_xw, // 3 - &_cimg_math_parser::mp_yh, // 4 - &_cimg_math_parser::mp_zd, // 5 - &_cimg_math_parser::mp_cs, // 6 - &_cimg_math_parser::mp_equal, // 7 - &_cimg_math_parser::mp_logical_or, // 8 - &_cimg_math_parser::mp_logical_and, // 9 - &_cimg_math_parser::mp_bitwise_or, // 10 - &_cimg_math_parser::mp_bitwise_and, // 11 - &_cimg_math_parser::mp_noteq, // 12 - &_cimg_math_parser::mp_eqeq, // 13 - &_cimg_math_parser::mp_infeq, // 14 - &_cimg_math_parser::mp_supeq, // 15 - &_cimg_math_parser::mp_inf, // 16 - &_cimg_math_parser::mp_sup, // 17 - &_cimg_math_parser::mp_lsl, // 18 - &_cimg_math_parser::mp_lsr, // 19 - &_cimg_math_parser::mp_sub, // 20 - &_cimg_math_parser::mp_add, // 21 - &_cimg_math_parser::mp_mul, // 22 - &_cimg_math_parser::mp_div, // 23 - &_cimg_math_parser::mp_modulo, // 24 - &_cimg_math_parser::mp_pow, // 25 - &_cimg_math_parser::mp_minus, // 26 - &_cimg_math_parser::mp_logical_not, // 27 - &_cimg_math_parser::mp_bitwise_not, // 28 - &_cimg_math_parser::mp_sin, // 29 - &_cimg_math_parser::mp_cos, // 30 - &_cimg_math_parser::mp_tan, // 31 - &_cimg_math_parser::mp_asin, // 32 - &_cimg_math_parser::mp_acos, // 33 - &_cimg_math_parser::mp_atan, // 34 - &_cimg_math_parser::mp_sinh, // 35 - &_cimg_math_parser::mp_cosh, // 36 - &_cimg_math_parser::mp_tanh, // 37 - &_cimg_math_parser::mp_log10, // 38 - &_cimg_math_parser::mp_log, // 39 - &_cimg_math_parser::mp_exp, // 40 - &_cimg_math_parser::mp_sqrt, // 41 - &_cimg_math_parser::mp_sign, // 42 - &_cimg_math_parser::mp_abs, // 43 - &_cimg_math_parser::mp_atan2, // 44 - &_cimg_math_parser::mp_if, // 45 - &_cimg_math_parser::mp_round, // 46 - &_cimg_math_parser::mp_ixyzc, // 47 - &_cimg_math_parser::mp_min, // 48 - &_cimg_math_parser::mp_max, // 49 - &_cimg_math_parser::mp_isnan, // 50 - &_cimg_math_parser::mp_isinf, // 51 - &_cimg_math_parser::mp_isint, // 52 - &_cimg_math_parser::mp_isbool, // 53 - &_cimg_math_parser::mp_rol, // 54 - &_cimg_math_parser::mp_ror, // 55 - &_cimg_math_parser::mp_sinc, // 56 - &_cimg_math_parser::mp_im, // 57 - &_cimg_math_parser::mp_iM, // 58 - &_cimg_math_parser::mp_ia, // 59 - &_cimg_math_parser::mp_iv, // 60 - &_cimg_math_parser::mp_xm, // 61 - &_cimg_math_parser::mp_ym, // 62 - &_cimg_math_parser::mp_zm, // 63 - &_cimg_math_parser::mp_cm, // 64 - &_cimg_math_parser::mp_xM, // 65 - &_cimg_math_parser::mp_yM, // 66 - &_cimg_math_parser::mp_zM, // 67 - &_cimg_math_parser::mp_cM, // 68 - &_cimg_math_parser::mp_arg, // 69 - &_cimg_math_parser::mp_int // 70 - }; + static double mp_gt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>_mp_arg(3)); + } - if (!mem) return 0; - mem[8] = x; mem[9] = y; mem[10] = z; mem[11] = c; - opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1; - cimglist_for(code,l) { - const CImg &op = code[l]; - opcode._data = op._data; opcode._height = op._height; // Allows to avoid parameter passing to evaluation functions. - mem[opcode(1)] = (this->*mp_funcs[opcode[0]])(); + 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 + *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_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + else for (mp.p_code = p_right; 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_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 bool boundary_conditions = (bool)_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); + else CImg(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::nan(); + } + + 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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.depth(); + } + + static double mp_image_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 &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_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 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_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 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 mem[result]; + return cimg::type::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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.height(); + } + + static double mp_image_im(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.min(); + } + + static double mp_image_iM(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.max(); + } + + static double mp_image_ia(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.mean(); + } + + static double mp_image_iv(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.variance(); + } + + static double mp_image_is(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.sum(); + } + + static double mp_image_ip(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.product(); + } + + static double mp_image_ic(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.median(); + } + + 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 &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_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::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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.spectrum(); + } + + 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 &img = ind==~0U?mp.imgin:mp.listin[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 &img = ind==~0U?mp.imgin:mp.listin[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 &img = ind==~0U?mp.imgin:mp.listin[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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + return (double)img.width()*img.height()*img.depth()*img.spectrum(); + } + + static double mp_image_xm(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0; + img.contains(img.min(),x); + return x; + } + + static double mp_image_xM(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0; + img.contains(img.max(),x); + return x; + } + + static double mp_image_ym(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0, y = 0; + img.contains(img.min(),x,y); + return y; + } + + static double mp_image_yM(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0, y = 0; + img.contains(img.max(),x,y); + return y; + } + + static double mp_image_zm(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0, y = 0, z = 0; + img.contains(img.min(),x,y,z); + return z; + } + + static double mp_image_zM(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0, y = 0, z = 0; + img.contains(img.max(),x,y,z); + return z; + } + + static double mp_image_cm(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0, y = 0, z = 0, c = 0; + img.contains(img.min(),x,y,z,c); + return c; + } + + static double mp_image_cM(_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 &img = ind==~0U?mp.imgin:mp.listin[ind]; + double x = 0, y = 0, z = 0, c = 0; + img.contains(img.max(),x,y,z,c); + return c; + } + + 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 &img = mp.imgin; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off::is_inf(_mp_arg(2)); + } + + static double mp_isint(_cimg_math_parser& mp) { + return (double)(cimg::mod(_mp_arg(2),1.0)==0); + } + + static double mp_isnan(_cimg_math_parser& mp) { + return (double)cimg::type::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 &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 = mp.imgin; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off &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 vals(i_end - 4); + double *p = vals.data(); + for (unsigned int i = 4; i &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=ptrb && (double)*ptr!=val) --ptr; + return ptr &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=ptr1b && *ptr1!=*ptr2b) --ptr1; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b); + return p2 &img = mp.listin[ind]; + const longT + off = (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &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 = mp.listin[ind]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &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::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 &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 &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=0 && y=0 && z=0 && c &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 &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=0 && y=0 && z=0 && c &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 &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::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 &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=0 && y=0 && z &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=0 && y=0 && z::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 &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 &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::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 &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=0 && y=0 && z &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=0 && y=0 && z::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_arg(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 &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::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::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::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::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::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 &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::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 &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::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::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::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::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::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 &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::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 *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_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 *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_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 val, vec; + CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); + CImg(ptrd,1,k,1,1,true) = val; + CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); + return cimg::type::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(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(); + return cimg::type::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(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); + return cimg::type::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(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_pseudoinvert(); + return cimg::type::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 U, S, V; + CImg(ptr1,k,l,1,1,true).SVD(U,S,V); + CImg(ptrd,k,l,1,1,true) = U; + CImg(ptrd + k*l,1,k,1,1,true) = S; + CImg(ptrd + k*l + k,k,k,1,1,true) = V; + return cimg::type::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=mp.mem.width()) + throw CImgArgumentException("[_cimg_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 &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_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.0f); + 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 - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double)); + else std::memmove(ptrd,ptrs,siz*sizeof(double)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (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 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 - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float)); + else std::memmove(ptrd,ptrs,siz*sizeof(float)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (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 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 vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; ires) 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; i0?res:0.0; + } + + static double mp_permutations(_cimg_math_parser& mp) { + return cimg::permutations(_mp_arg(2),_mp_arg(3),(bool)_mp_arg(4)); + } + + 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); + cimg_pragma_openmp(critical) + { + CImg expr(mp.opcode[2] - 3); + const ulongT *ptrs = mp.opcode._data + 3; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(expr); + cimg::mutex(6); + std::fprintf(cimg::output(),"\n[_cimg_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::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(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); + return cimg::type::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 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::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 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::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 &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &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=0 && y=0 && z=0 && c &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 &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=0 && y=0 && z=0 && c &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 &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::nan(); + } + + static double mp_set_Ixyz_s(_cimg_math_parser& mp) { + CImg &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=0 && y=0 && z &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=0 && y=0 && z::nan(); + } + + static double mp_set_Joff_s(_cimg_math_parser& mp) { + CImg &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 &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::nan(); + } + + static double mp_set_Jxyz_s(_cimg_math_parser& mp) { + CImg &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=0 && y=0 && z &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=0 && y=0 && z::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_single(_cimg_math_parser& mp) { + const double res = _mp_arg(1); + cimg_pragma_openmp(critical) + { + for (const CImg *const p_end = ++mp.p_code + mp.opcode[2]; + 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_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(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,true).get_solve(CImg(ptr1,k,l,1,1,true)); + return cimg::type::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(ptrd,chunk_siz,siz/chunk_siz,1,1,true) = CImg(ptrs,chunk_siz,siz/chunk_siz,1,1,true). + get_sort(is_increasing,chunk_siz>1?'y':0); + return cimg::type::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_std(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i0) mp.mem[ptrd++] = (double)*(ptrs++); + return cimg::type::nan(); + } + + static double mp_stod(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2); + const unsigned int siz = (unsigned int)mp.opcode[3]; + const bool is_strict = (bool)_mp_arg(4); + if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':cimg::type::nan(); + CImg ss(siz + 1); + double val = cimg::type::nan(); + char sep; + for (unsigned i = 0; i::inf(); err = 1; } + else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type::nan(); err = 1; } + if (err==1 && !is_positive) val = -val; + } +#endif + if (is_strict && err!=1) return cimg::type::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(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(ptrd,l,k,1,1,true) = CImg(ptrs,k,l,1,1,true).get_transpose(); + return cimg::type::nan(); + } + + static double mp_u(_cimg_math_parser& mp) { + return cimg::rand(_mp_arg(2),_mp_arg(3)); + } + + static double mp_uppercase(_cimg_math_parser& mp) { + return cimg::uppercase(_mp_arg(2)); + } + + static double mp_variance(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i::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_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::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::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::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 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::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 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::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 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::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 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::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 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::nan(); + } + + static double mp_vector_neq(_cimg_math_parser& mp) { + return !mp_vector_eq(mp); + } + + static double mp_vector_print(_cimg_math_parser& mp) { + cimg_pragma_openmp(critical) + { + CImg expr(mp.opcode[2] - 4); + const ulongT *ptrs = mp.opcode._data + 4; + 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_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; + } + std::fprintf(cimg::output()," ] (size: %u)",siz0); + std::fflush(cimg::output()); + cimg::mutex(6,0); + } + return cimg::type::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(ptrd,p1,1,1,1,true) = CImg(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(ptrd,p1,1,1,1,true) = CImg(1,1,1,1,value).resize(p1,1,1,1,interpolation, + boundary_conditions); + } + return cimg::type::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(ptrd,p1,1,1,1,true) = CImg(ptrs,p1,1,1,1,true).get_mirror('x'); + return cimg::type::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_whiledo(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *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(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::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_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_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 &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::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::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::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::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::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 &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::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 &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::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::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::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::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::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 &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::nan(); + } + +#undef _mp_arg + + }; // struct _cimg_math_parser {} //! Compute the square value of each pixel value. /** @@ -14042,21 +23001,17 @@ namespace cimg_library { \note - The \inplace of this method statically casts the computed values to the pixel type \c T. - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. - \par Sample code : + \par Example \code const CImg img("reference.jpg"); (img,img.get_sqr().normalize(0,255)).display(); \endcode \image html ref_sqr.jpg - \sa get_sqr() const, - sqrt(), - pow(), - exp(), - log(), - log10(). **/ CImg& sqr() { - cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); }; + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); }; return *this; } @@ -14071,21 +23026,17 @@ namespace cimg_library { \note - The \inplace of this method statically casts the computed values to the pixel type \c T. - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. - \par Sample code : + \par Example \code const CImg img("reference.jpg"); (img,img.get_sqrt().normalize(0,255)).display(); \endcode \image html ref_sqrt.jpg - \sa get_sqrt() const,, - sqr(), - pow(), - exp(), - log(), - log10(). **/ CImg& sqrt() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd); return *this; } @@ -14100,15 +23051,11 @@ namespace cimg_library { \note - The \inplace of this method statically casts the computed values to the pixel type \c T. - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. - \sa get_exp() const, - sqr(), - sqrt(), - pow(), - log(), - log10(). **/ CImg& exp() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd); return *this; } @@ -14119,19 +23066,16 @@ namespace cimg_library { //! 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$log_e(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_log() const, - sqr(), - sqrt(), - pow(), - exp(), - log10(). **/ CImg& log() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd); return *this; } @@ -14140,21 +23084,38 @@ namespace cimg_library { return CImg(*this,false).log(); } - //! Compute the base-10 logarithm of each pixel value. + //! 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-10 logarithm \f$log_{10}(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. + **/ + CImg& log2() { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096)) + cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd); + return *this; + } + + //! Compute the base-10 logarithm of each pixel value \newinstance. + CImg get_log2() const { + return CImg(*this,false).log2(); + } + + //! 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 image, if the pixel type \c T is \e not float-valued. - \sa get_log10(), - sqr(), - sqrt(), - pow(), - exp(), - log(). **/ CImg& log10() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd); return *this; } @@ -14169,11 +23130,11 @@ namespace cimg_library { \note - The \inplace of this method statically casts the computed values to the pixel type \c T. - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. - \sa get_abs(), - sign(). **/ CImg& abs() { - cimg_for(*this,ptrd,T) *ptrd = cimg::abs(*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288)) + cimg_rof(*this,ptrd,T) *ptrd = cimg::abs(*ptrd); return *this; } @@ -14184,19 +23145,20 @@ namespace cimg_library { //! 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$sign(I_{(x,y,z,c)})\f$. + 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 : + - 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 image, if the pixel type \c T is \e not float-valued. - \sa get_sign(), - abs(). **/ CImg& sign() { - cimg_for(*this,ptrd,T) *ptrd = cimg::sign(*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) *ptrd = cimg::sign(*ptrd); return *this; } @@ -14207,18 +23169,16 @@ namespace cimg_library { //! 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$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_cos(), - sin(), - sinc(), - tan(). **/ CImg& cos() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd); return *this; } @@ -14229,18 +23189,16 @@ namespace cimg_library { //! 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$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_sin(), - cos(), - sinc(), - tan(). **/ CImg& sin() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd); return *this; } @@ -14251,18 +23209,17 @@ namespace cimg_library { //! 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$sinc(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_sinc(), - cos(), - sin(), - tan(). **/ CImg& sinc() { - cimg_for(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048)) + cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd); return *this; } @@ -14273,18 +23230,16 @@ namespace cimg_library { //! 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$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_tan(), - cos(), - sin(), - sinc(). **/ CImg& tan() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd); return *this; } @@ -14295,16 +23250,16 @@ namespace cimg_library { //! 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$cosh(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_cosh(), - sinh(), - tanh(). **/ CImg& cosh() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd); return *this; } @@ -14315,16 +23270,16 @@ namespace cimg_library { //! 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$sinh(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_sinh(), - cosh(), - tanh(). **/ CImg& sinh() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd); return *this; } @@ -14335,16 +23290,16 @@ namespace cimg_library { //! 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$tanh(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_tanh(), - cosh(), - sinh(). **/ CImg& tanh() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd); return *this; } @@ -14355,16 +23310,16 @@ namespace cimg_library { //! 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$acos(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_acos(), - asin(), - atan(). **/ CImg& acos() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd); return *this; } @@ -14375,16 +23330,16 @@ namespace cimg_library { //! 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$asin(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_asin(), - acos(), - atan(). **/ CImg& asin() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd); return *this; } @@ -14395,16 +23350,16 @@ namespace cimg_library { //! 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$atan(I_{(x,y,z,c)})\f$. + 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 image, if the pixel type \c T is \e not float-valued. - \sa get_atan(), - acos(), - asin(). **/ CImg& atan() { - cimg_for(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd); return *this; } @@ -14415,12 +23370,13 @@ namespace cimg_library { //! 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$atan2(I_{(x,y,z,c)})\f$. - \param img : The image whose pixel values specify the second argument of the \c atan2() function. + 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 image, if the pixel type \c T is \e not float-valued. - \par Sample code : + \par Example \code const CImg img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2'. @@ -14428,18 +23384,16 @@ namespace cimg_library { img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value. (img_x,img_y,img_atan2).display(); \endcode - \image html ref_atan2.jpg - \sa get_atan2(), - atan(). **/ template CImg& atan2(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs&), except that it performs a pointwise multiplication instead of an addition. + - Similar to operator+=(const CImg&), 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&) instead. - \par Sample code : + \par Example \code CImg img("reference.jpg"), @@ -14466,19 +23421,16 @@ namespace cimg_library { shade.normalize(0,1); (img,shade,img.get_mul(shade)).display(); \endcode - \image html ref_mul.jpg - \sa get_mul(), - div(), - operator*=(const CImg&). **/ template CImg& mul(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs&), except that it performs a pointwise division instead of a multiplication. - \sa get_div(const CImg&) const, - operator/=(const CImg&) **/ template CImg& div(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs image, if the pixel type \c T is \e not float-valued. - \par Sample code : + \par Example \code const CImg img0("reference.jpg"), // Load reference color image. @@ -14530,24 +23481,51 @@ namespace cimg_library { img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5. (img0,img1,img2).display(); \endcode - \image html ref_pow.jpg - \sa get_pow(double) const, - pow(const char*), - pow(const CImg&), - sqr(), - sqrt(), - exp(), - log(), - log10(). **/ CImg& pow(const double p) { - if (p==0) return fill(1); - if (p==0.5) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)std::sqrt((double)val); } return *this; } + if (is_empty()) return *this; + if (p==-4) { + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); } + return *this; + } + if (p==-3) { + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); } + return *this; + } + if (p==-2) { + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); } + return *this; + } + if (p==-1) { + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); } + return *this; + } + if (p==-0.5) { + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); } + return *this; + } + if (p==0) return fill((T)1); + if (p==0.25) return sqrt().sqrt(); + if (p==0.5) return sqrt(); if (p==1) return *this; - if (p==2) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val; } return *this; } - if (p==3) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; } - if (p==4) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; } - cimg_for(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p); + if (p==2) return sqr(); + if (p==3) { + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } + return *this; + } + if (p==4) { + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=131072)) + cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } + return *this; + } + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=1024)) + cimg_rof(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p); return *this; } @@ -14559,30 +23537,9 @@ namespace cimg_library { //! 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. - \sa get_pow(const char*) const, - pow(double), - pow(CImg&). **/ CImg& pow(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"pow"); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode() = omode; - values.load(expression); - } - pow(values); - } - cimg::exception_mode() = omode; - return *this; + return pow((+*this)._fill(expression,true,true,0,0,"pow",this)); } //! Raise each pixel value to a power, specified from an expression \newinstance. @@ -14593,18 +23550,16 @@ namespace cimg_library { //! Raise each pixel value to a power, pointwisely specified from another image. /** Similar to operator+=(const CImg& img), except that it performs an exponentiation instead of an addition. - \sa get_pow(const CImg&) const, - pow(double), - pow(const char*). **/ template CImg& pow(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs&), - operator<<=(unsigned int), - operator>>=(unsigned int). **/ CImg& rol(const unsigned int n=1) { - cimg_for(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n); return *this; } @@ -14638,32 +23590,9 @@ namespace cimg_library { //! 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. - \sa get_rol(const char*) const, - rol(unsigned int), - rol(const CImg&), - operator<<=(const char*), - operator>>=(const char*). **/ CImg& rol(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"rol"); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode() = omode; - values.load(expression); - } - rol(values); - } - cimg::exception_mode() = omode; - return *this; + return rol((+*this)._fill(expression,true,true,0,0,"rol",this)); } //! Compute the bitwise left rotation of each pixel value \newinstance. @@ -14674,20 +23603,16 @@ namespace cimg_library { //! Compute the bitwise left rotation of each pixel value. /** Similar to operator<<=(const CImg&), except that it performs a left rotation instead of a left shift. - \sa get_rol(const CImg&) const, - rol(unsigned int), - rol(const char*), - operator<<=(const CImg&), - operator>>=(const CImg&). **/ template CImg& rol(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs>=(unsigned int), except that it performs a right rotation instead of a right shift. - \sa get_ror(unsigned int) const, - ror(const char*), - ror(const CImg&), - operator<<=(unsigned int), - operator>>=(unsigned int). **/ CImg& ror(const unsigned int n=1) { - cimg_for(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n); + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n); return *this; } @@ -14721,32 +23643,9 @@ namespace cimg_library { //! 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. - \sa get_ror(const char*) const, - ror(unsigned int), - ror(const CImg&), - operator<<=(const char*), - operator>>=(const char*). **/ CImg& ror(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"ror"); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode() = omode; - values.load(expression); - } - ror(values); - } - cimg::exception_mode() = omode; - return *this; + return ror((+*this)._fill(expression,true,true,0,0,"ror",this)); } //! Compute the bitwise right rotation of each pixel value \newinstance. @@ -14757,20 +23656,16 @@ namespace cimg_library { //! Compute the bitwise right rotation of each pixel value. /** Similar to operator>>=(const CImg&), except that it performs a right rotation instead of a right shift. - \sa get_ror(const CImg&), - ror(unsigned int), - ror(const char *), - operator<<=(const CImg&), - operator>>=(const CImg&). **/ template CImg& ror(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& min(const T val) { - cimg_for(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val); + //! 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& min(const T& val) { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536)) + cimg_rof(*this,ptrd,T) *ptrd = std::min(*ptrd,val); return *this; } - CImg get_min(const T val) const { + //! Pointwise min operator between instance image and a value \newinstance. + CImg get_min(const T& val) const { return (+*this).min(val); } //! 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 CImg& min(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg<_cimg_Tt> get_min(const CImg& img) const { return CImg<_cimg_Tt>(*this,false).min(img); } - //! Pointwise min operator between an image and a string. + //! 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& min(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"min"); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode() = omode; - values.load(expression); - } - min(values); - } - cimg::exception_mode() = omode; - return *this; + return min((+*this)._fill(expression,true,true,0,0,"min",this)); } + //! Pointwise min operator between an image and an expression \newinstance. CImg get_min(const char *const expression) const { return CImg(*this,false).min(expression); } - //! Pointwise max operator between an image and a value. - CImg& max(const T val) { - cimg_for(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val); + //! 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& max(const T& val) { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536)) + cimg_rof(*this,ptrd,T) *ptrd = std::max(*ptrd,val); return *this; } - CImg get_max(const T val) const { + //! Pointwise max operator between instance image and a value \newinstance. + CImg get_max(const T& val) const { return (+*this).max(val); } //! 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 CImg& max(const CImg& img) { - const unsigned int siz = size(), isiz = img.size(); + 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 (unsigned int n = siz/isiz; n; --n) - for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrsisiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg<_cimg_Tt> get_max(const CImg& img) const { return CImg<_cimg_Tt>(*this,false).max(img); } - //! Pointwise max operator between an image and a string. + //! 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& max(const char *const expression) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"max"); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode() = omode; - values.load(expression); - } - max(values); - } - cimg::exception_mode() = omode; - return *this; + return max((+*this)._fill(expression,true,true,0,0,"max",this)); } + //! Pointwise max operator between an image and an expression \newinstance. CImg get_max(const char *const expression) const { return CImg(*this,false).max(expression); } - //! Return a reference to the minimum pixel value of the image instance + //! Return a reference to the minimum pixel value. + /** + **/ T& min() { if (is_empty()) throw CImgInstanceException(_cimg_instance - "min() : Empty instance.", + "min(): Empty instance.", cimg_instance); T *ptr_min = _data; T min_value = *ptr_min; @@ -14906,10 +23809,11 @@ namespace cimg_library { 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.", + "min(): Empty instance.", cimg_instance); const T *ptr_min = _data; T min_value = *ptr_min; @@ -14917,11 +23821,13 @@ namespace cimg_library { return *ptr_min; } - //! Return a reference to the maximum pixel value of the image instance + //! Return a reference to the maximum pixel value. + /** + **/ T& max() { if (is_empty()) throw CImgInstanceException(_cimg_instance - "max() : Empty instance.", + "max(): Empty instance.", cimg_instance); T *ptr_max = _data; T max_value = *ptr_max; @@ -14929,10 +23835,11 @@ namespace cimg_library { 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.", + "max(): Empty instance.", cimg_instance); const T *ptr_max = _data; T max_value = *ptr_max; @@ -14940,12 +23847,15 @@ namespace cimg_library { return *ptr_max; } - //! Return a reference to the minimum pixel value and return also the maximum pixel value. + //! Return a reference to the minimum pixel value as well as the maximum pixel value. + /** + \param[out] max_val Maximum pixel value. + **/ template T& min_max(t& max_val) { if (is_empty()) throw CImgInstanceException(_cimg_instance - "min_max() : Empty instance.", + "min_max(): Empty instance.", cimg_instance); T *ptr_min = _data; T min_value = *ptr_min, max_value = min_value; @@ -14958,11 +23868,12 @@ namespace cimg_library { return *ptr_min; } + //! Return a reference to the minimum pixel value as well as the maximum pixel value \const. template const T& min_max(t& max_val) const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "min_max() : Empty instance.", + "min_max(): Empty instance.", cimg_instance); const T *ptr_min = _data; T min_value = *ptr_min, max_value = min_value; @@ -14975,12 +23886,15 @@ namespace cimg_library { return *ptr_min; } - //! Return a reference to the maximum pixel value and return also the minimum pixel value. + //! Return a reference to the maximum pixel value as well as the minimum pixel value. + /** + \param[out] min_val Minimum pixel value. + **/ template T& max_min(t& min_val) { if (is_empty()) throw CImgInstanceException(_cimg_instance - "max_min() : Empty instance.", + "max_min(): Empty instance.", cimg_instance); T *ptr_max = _data; T max_value = *ptr_max, min_value = max_value; @@ -14993,11 +23907,12 @@ namespace cimg_library { return *ptr_max; } + //! Return a reference to the maximum pixel value as well as the minimum pixel value \const. template const T& max_min(t& min_val) const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "max_min() : Empty instance.", + "max_min(): Empty instance.", cimg_instance); const T *ptr_max = _data; T max_value = *ptr_max, min_value = max_value; @@ -15010,134 +23925,160 @@ namespace cimg_library { return *ptr_max; } - //! Return the kth smallest element of the image. - T kth_smallest(const unsigned int k) const { + //! 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.", + "kth_smallest(): Empty instance.", cimg_instance); CImg arr(*this); - unsigned int l = 0, ir = size() - 1; - for (;;) { - if (ir<=l+1) { - if (ir==l+1 && arr[ir]>1; - cimg::swap(arr[mid],arr[l+1]); + 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]); - unsigned int i = l + 1, j = ir; - const T pivot = arr[l+1]; - for (;;) { + 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); if (j=k) ir = j - 1; if (j<=k) l = i; } } - return 0; } - //! Return the median value of the image. + //! Return the median pixel value. + /** + **/ T median() const { - const unsigned int s = size(); - const T res = kth_smallest(s>>1); - return (s%2)?res:((res+kth_smallest((s>>1)-1))/2); - } - - //! Return the sum of all the pixel values in an image. - Tdouble sum() const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "sum() : Empty instance.", + "median(): Empty instance.", cimg_instance); - Tdouble res = 0; - cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs; + 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 mean pixel value of the image instance. - Tdouble mean() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "mean() : Empty instance.", - cimg_instance); - Tdouble res = 0; - cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs; + //! 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 image. + //! Return the variance of the pixel values. /** - @param variance_method Determines how to calculate the variance - - - - - - - - - -
0Second moment: - @f$ v = 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$
1Best unbiased estimator: @f$ v = \frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 @f$
2Least median of squares
3Least trimmed of squares
- */ - Tdouble variance(const unsigned int variance_method=1) const { - Tdouble foo; + \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 and the mean of the image. + //! 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 - Tdouble variance_mean(const unsigned int variance_method, t& mean) const { + double variance_mean(const unsigned int variance_method, t& mean) const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "variance() : Empty instance.", + "variance_mean(): Empty instance.", cimg_instance); - Tdouble variance = 0, average = 0; - const unsigned int siz = size(); + double variance = 0, average = 0; + const ulongT siz = size(); switch (variance_method) { - case 0 :{ // Least mean square (standard definition) - Tdouble S = 0, S2 = 0; - cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; } + 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) - Tdouble S = 0, S2 = 0; - cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; } + 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 buf(*this); + CImg buf(*this,false); buf.sort(); - const unsigned int siz2 = siz>>1; - const Tdouble med_i = (double)buf[siz2]; - cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; } + 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 Tdouble sig = (Tdouble)(1.4828*buf[siz2]); + const double sig = (double)(1.4828*buf[siz2]); variance = sig*sig; } break; default : { // Least trimmed of Squares - CImg buf(*this); - const unsigned int siz2 = siz>>1; - cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; } + CImg 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(); - Tdouble a = 0; + double a = 0; const Tfloat *ptrs = buf._data; - for (unsigned int j = 0; j0?variance:0; } - //! Estimate noise variance of the image instance. + //! Return estimated variance of the noise. /** - \param variance_method : method to compute the variance + \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. - \see variance() **/ - Tdouble variance_noise(const unsigned int variance_method=2) const { - const unsigned int siz = size(); + 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 tmp(*this); if (_depth==1) { - const Tdouble cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed. - CImg_3x3(I,T); - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { - tmp(x,y,c) = cste*((Tdouble)Inc + (Tdouble)Ipc + (Tdouble)Icn + - (Tdouble)Icp - 4*(Tdouble)Icc); + const double cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed. + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=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 Tdouble cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed. - CImg_3x3x3(I,T); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { - tmp(x,y,z,c) = cste*( - (Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + (Tdouble)Icpc + - (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc); + const double cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed. + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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. - Tdouble variance = 0, S = 0, S2 = 0; + double variance = 0, S = 0, S2 = 0; if (_depth==1) { - const Tdouble cste = 1.0/std::sqrt(20.0); + const double cste = 1.0/std::sqrt(20.0); CImg_3x3(I,T); cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { - const Tdouble val = cste*((Tdouble)Inc + (Tdouble)Ipc + - (Tdouble)Icn + (Tdouble)Icp - 4*(Tdouble)Icc); + const double val = cste*((double)Inc + (double)Ipc + + (double)Icn + (double)Icp - 4*(double)Icc); S+=val; S2+=val*val; } } else { - const Tdouble cste = 1.0/std::sqrt(42.0); + const double cste = 1.0/std::sqrt(42.0); CImg_3x3x3(I,T); cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { - const Tdouble val = cste * - ((Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + - (Tdouble)Icpc + - (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc); + const double val = cste * + ((double)Incc + (double)Ipcc + (double)Icnc + + (double)Icpc + + (double)Iccn + (double)Iccp - 6*(double)Iccc); S+=val; S2+=val*val; } } @@ -15206,66 +24157,212 @@ namespace cimg_library { } //! Compute the MSE (Mean-Squared Error) between two images. + /** + \param img Image used as the second argument of the MSE operator. + **/ template - Tdouble MSE(const CImg& img) const { + double MSE(const CImg& img) const { if (img.size()!=size()) throw CImgArgumentException(_cimg_instance - "MSE() : Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.", + "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); - Tdouble vMSE = 0; - const t* ptr2 = img.end(); + double vMSE = 0; + const t* ptr2 = img._data; cimg_for(*this,ptr1,T) { - const Tdouble diff = (Tdouble)*ptr1 - (Tdouble)*(--ptr2); + const double diff = (double)*ptr1 - (double)*(ptr2++); vMSE+=diff*diff; } - vMSE/=img.size(); + const ulongT siz = img.size(); + if (siz) vMSE/=siz; return vMSE; } - //! Compute the PSNR between two images. - template - Tdouble PSNR(const CImg& img, const Tdouble valmax=255) const { - const Tdouble vMSE = (Tdouble)std::sqrt(MSE(img)); - return (vMSE!=0)?(Tdouble)(20*std::log10(valmax/vMSE)):(Tdouble)(cimg::type::max()); - } - - //! Evaluate math expression. + //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images. /** - If you make successive evaluations on the same image and with the same expression, - you can set 'expr' to 0 after the first call, to skip the math parsing step. + \param img Image used as the second argument of the PSNR operator. + \param max_value Maximum theoretical value of the signal. + **/ + template + double PSNR(const CImg& 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::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 { - static _cimg_math_parser *mp = 0; - if (expression) { delete mp; mp = 0; mp = new _cimg_math_parser(*this,expression,"eval"); } - if (!mp) - throw CImgArgumentException(_cimg_instance - "eval() : No expression has been previously specified.", - cimg_instance); - return mp->eval(x,y,z,c); + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); } - //! Compute a statistics vector (min,max,mean,variance,xmin,ymin,zmin,cmin,xmax,ymax,zmax,cmax). - CImg& stats(const unsigned int variance_method=1) { - return get_stats(variance_method).move_to(*this); + //! 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 *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,x,y,z,c,list_inputs,list_outputs); } + double _eval(CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!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 + void eval(CImg &output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + template + void eval(CImg& output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); + } + + template + void _eval(CImg& output, CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression) { output.assign(1); *output = 0; } + if (!expression[1]) switch (*expression) { // Single-char optimization. + case 'w' : output.assign(1); *output = (t)_width; + case 'h' : output.assign(1); *output = (t)_height; + case 'd' : output.assign(1); *output = (t)_depth; + case 's' : output.assign(1); *output = (t)_spectrum; + case 'r' : output.assign(1); *output = (t)_is_shared; + } + _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 + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,xyzc,list_inputs,list_outputs); + } + + //! Evaluate math formula on a set of variables \const. + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,xyzc,list_inputs,list_outputs); + } + + template + CImg _eval(CImg *const output, const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + CImg res(1,xyzc.size()/4); + if (!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[min; max; mean; variance; xmin; ymin; zmin; cmin; xmax; ymax; zmax; cmax; sum; product]
. + **/ CImg get_stats(const unsigned int variance_method=1) const { if (is_empty()) return CImg(); - const unsigned int siz = size(); - const T *const odata = _data; - const T *pm = odata, *pM = odata; - Tdouble S = 0, S2 = 0; - T m = *pm, M = m; - cimg_for(*this,ptrs,T) { - const T val = *ptrs; - const Tdouble _val = (Tdouble)val; - if (valM) { M = val; pM = ptrs; } - S+=_val; - S2+=_val*_val; + const T *const p_end = end(), *pm = _data, *pM = _data; + double S = 0, S2 = 0, P = 1; + const ulongT siz = size(); + T m = *pm, M = *pM; + + cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if(siz>=131072)) { + const T *lpm = _data, *lpM = _data; + T lm = *lpm, lM = *lpM; + cimg_pragma_openmp(for) + for (const T *ptrs = _data; ptrslM) { lM = val; lpM = ptrs; } + S+=_val; + S2+=_val*_val; + P*=_val; + } + cimg_pragma_openmp(critical) { + if (lmM || (lM==M && lpM1?(S2 - S*S/siz)/(siz - 1):0): @@ -15276,9 +24373,15 @@ namespace cimg_library { xM = 0, yM = 0, zM = 0, cM = 0; contains(*pm,xm,ym,zm,cm); contains(*pM,xM,yM,zM,cM); - return CImg(1,12).fill((Tdouble)m,(Tdouble)M,mean_value,variance_value, - (Tdouble)xm,(Tdouble)ym,(Tdouble)zm,(Tdouble)cm, - (Tdouble)xM,(Tdouble)yM,(Tdouble)zM,(Tdouble)cM); + return CImg(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& stats(const unsigned int variance_method=1) { + return get_stats(variance_method).move_to(*this); } //@} @@ -15288,54 +24391,64 @@ namespace cimg_library { //@{ //------------------------------------- - //! Return the norm of the current vector/matrix. \p ntype = norm type (0=L2, 1=L1, -1=Linf). - Tdouble magnitude(const int magnitude_type=2) const { + //! Compute norm of the image, viewed as a matrix. + /** + \param magnitude_type Norm type. Can be: + - \c -1: Linf-norm + - \c 0: L2-norm + - \c 1: L1-norm + **/ + double magnitude(const int magnitude_type=2) const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "magnitude() : Empty instance.", + "magnitude(): Empty instance.", cimg_instance); - Tdouble res = 0; + double res = 0; switch (magnitude_type) { case -1 : { - cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)cimg::abs(*ptrs); if (val>res) res = val; } + 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+=(Tdouble)cimg::abs(*ptrs); + cimg_for(*this,ptrs,T) res+=(double)cimg::abs(*ptrs); } break; default : { - cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::sqr(*ptrs); - res = (Tdouble)std::sqrt(res); + cimg_for(*this,ptrs,T) res+=(double)cimg::sqr(*ptrs); + res = (double)std::sqrt(res); } } return res; } - //! Return the trace of the image, viewed as a matrix. - Tdouble trace() const { + //! Compute the trace of the image, viewed as a matrix. + /** + **/ + double trace() const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "trace() : Empty instance.", + "trace(): Empty instance.", cimg_instance); - Tdouble res = 0; - cimg_forX(*this,k) res+=(Tdouble)(*this)(k,k); + double res = 0; + cimg_forX(*this,k) res+=(double)(*this)(k,k); return res; } - //! Return the determinant of the image, viewed as a matrix. - Tdouble det() const { + //! 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 empty or not a square matrix.", + "det(): Instance is not a square matrix.", cimg_instance); switch (_width) { - case 1 : return (Tdouble)((*this)(0,0)); - case 2 : return (Tdouble)((*this)(0,0))*(Tdouble)((*this)(1,1)) - (Tdouble)((*this)(0,1))*(Tdouble)((*this)(1,0)); + 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 Tdouble - a = (Tdouble)_data[0], d = (Tdouble)_data[1], g = (Tdouble)_data[2], - b = (Tdouble)_data[3], e = (Tdouble)_data[4], h = (Tdouble)_data[5], - c = (Tdouble)_data[6], f = (Tdouble)_data[7], i = (Tdouble)_data[8]; + 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 : { @@ -15343,86 +24456,126 @@ namespace cimg_library { CImg indx; bool d; lu._LU(indx,d); - Tdouble res = d?(Tdouble)1:(Tdouble)-1; + double res = d?(double)1:(double)-1; cimg_forX(lu,i) res*=lu(i,i); return res; } } - return 0; } - //! Return the dot product of the current vector/matrix with the vector/matrix \p img. + //! Compute the dot product between instance and argument, viewed as matrices. + /** + \param img Image used as a second argument of the dot product. + **/ template - Tdouble dot(const CImg& img) const { + double dot(const CImg& img) const { if (is_empty()) throw CImgInstanceException(_cimg_instance - "dot() : Empty instance.", + "dot(): Empty instance.", cimg_instance); if (!img) throw CImgArgumentException(_cimg_instance - "dot() : Empty specified image.", + "dot(): Empty specified image.", cimg_instance); - const unsigned int nb = cimg::min(size(),img.size()); - Tdouble res = 0; - for (unsigned int off = 0; off get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { - _cimg_static CImg res; + CImg res; if (res._height!=_spectrum) res.assign(1,_spectrum); - const unsigned int whd = _width*_height*_depth; + 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; } - //! Return a new image corresponding to the \a square \a matrix located at (\p x,\p y,\p z) of the current vector-valued image. + //! 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 get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { const int n = (int)std::sqrt((double)_spectrum); const T *ptrs = data(x,y,z,0); - const unsigned int whd = _width*_height*_depth; + const ulongT whd = (ulongT)_width*_height*_depth; CImg res(n,n); T *ptrd = res._data; cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } return res; } - //! Return a new image corresponding to the \a diffusion \a tensor located at (\p x,\p y,\p z) of the current vector-valued image. + //! 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 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 unsigned int whd = _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)); + 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 the image \p vec as the \a vector \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + //! 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 CImg& set_vector_at(const CImg& 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 unsigned int whd = _width*_height*_depth; + const ulongT whd = (ulongT)_width*_height*_depth; T *ptrd = data(x,y,z); - for (unsigned int k = cimg::min((unsigned int)vec.size(),_spectrum); k; --k) { *ptrd = (T)*(ptrs++); ptrd+=whd; } + for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) { + *ptrd = (T)*(ptrs++); ptrd+=whd; + } } return *this; } - //! Set the image \p vec as the \a square \a matrix-valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + //! 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 CImg& set_matrix_at(const CImg& 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 the image \p vec as the \a tensor \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + //! 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 CImg& set_tensor_at(const CImg& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { T *ptrd = data(x,y,z,0); - const unsigned int siz = _width*_height*_depth; + const ulongT siz = (ulongT)_width*_height*_depth; if (ten._height==2) { *ptrd = (T)ten[0]; ptrd+=siz; *ptrd = (T)ten[1]; ptrd+=siz; @@ -15439,18 +24592,24 @@ namespace cimg_library { return *this; } - //! Unroll all images values into a one-column vector. + //! Unroll pixel values along axis \c y. + /** + \note Equivalent to \code unroll('y'); \endcode. + **/ CImg& vector() { return unroll('y'); } + //! Unroll pixel values along axis \c y \newinstance. CImg get_vector() const { return get_unroll('y'); } - //! Realign pixel values of the image instance as a square matrix + //! Resize image to become a scalar square matrix. + /** + **/ CImg& matrix() { - const unsigned int siz = size(); + const ulongT siz = size(); switch (siz) { case 1 : break; case 4 : _width = _height = 2; break; @@ -15463,11 +24622,11 @@ namespace cimg_library { case 81 : _width = _height = 9; break; case 100 : _width = _height = 10; break; default : { - unsigned int i = 11, i2 = i*i; + ulongT i = 11, i2 = i*i; while (i2 get_matrix() const { return (+*this).matrix(); } - //! Realign pixel values of the image instance as a symmetric tensor. + //! Resize image to become a symmetric tensor. + /** + **/ CImg& tensor() { return get_tensor().move_to(*this); } + //! Resize image to become a symmetric tensor \newinstance. CImg get_tensor() const { CImg res; - const unsigned int siz = size(); + const ulongT siz = size(); switch (siz) { case 1 : break; case 3 : @@ -15506,50 +24669,68 @@ namespace cimg_library { break; default : throw CImgInstanceException(_cimg_instance - "tensor() : Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).", + "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).", cimg_instance); } return res; } - //! Get a diagonal matrix, whose diagonal coefficients are the coefficients of the input image. + //! 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& diagonal() { return get_diagonal().move_to(*this); } + //! Resize image to become a diagonal matrix \newinstance. CImg get_diagonal() const { if (is_empty()) return *this; - CImg res(size(),size(),1,1,0); - cimg_foroff(*this,off) res(off,off) = (*this)(off); + const unsigned int siz = (unsigned int)size(); + CImg res(siz,siz,1,1,0); + cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off]; return res; } - //! Get an identity matrix having same dimension than image instance. + //! 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& identity_matrix() { - return identity_matrix(cimg::max(_width,_height)).move_to(*this); + return identity_matrix(std::max(_width,_height)).move_to(*this); } + //! Replace the image by an identity matrix \newinstance. CImg get_identity_matrix() const { - return identity_matrix(cimg::max(_width,_height)); + return identity_matrix(std::max(_width,_height)); } - //! Return a N-numbered sequence vector from \p a0 to \p a1. - CImg& sequence(const T a0, const T a1) { + //! Fill image with a linear sequence of values. + /** + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + CImg& sequence(const T& a0, const T& a1) { if (is_empty()) return *this; - const unsigned int siz = size() - 1; + const ulongT siz = size() - 1; T* ptr = _data; if (siz) { - const Tdouble delta = (Tdouble)a1 - (Tdouble)a0; + const double delta = (double)a1 - (double)a0; cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); } else *ptr = a0; return *this; } - CImg get_sequence(const T a0, const T a1) const { + //! Fill image with a linear sequence of values \newinstance. + CImg get_sequence(const T& a0, const T& a1) const { return (+*this).sequence(a0,a1); } - //! Transpose the current matrix. + //! Transpose the image, viewed as a matrix. + /** + \note Equivalent to \code permute_axes("yxzc"); \endcode + **/ CImg& transpose() { if (_width==1) { _width = _height; _height = 1; return *this; } if (_height==1) { _height = _width; _width = 1; return *this; } @@ -15560,16 +24741,21 @@ namespace cimg_library { return get_transpose().move_to(*this); } + //! Transpose the image, viewed as a matrix \newinstance. CImg get_transpose() const { return get_permute_axes("yxzc"); } - //! Compute the cross product between two 3d vectors. + //! 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 CImg& cross(const CImg& 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.", + "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); @@ -15580,38 +24766,44 @@ namespace cimg_library { return *this; } + //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors \newinstance. template CImg<_cimg_Tt> get_cross(const CImg& img) const { return CImg<_cimg_Tt>(*this).cross(img); } - //! Invert the current matrix. + //! 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& invert(const bool use_LU=true) { if (_width!=_height || _depth!=1 || _spectrum!=1) throw CImgInstanceException(_cimg_instance - "invert() : Instance is not a square matrix.", + "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_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.", + "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.", + "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); + 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.0:det(); @@ -15653,48 +24845,63 @@ namespace cimg_library { return *this; } + //! Invert the instance image, viewed as a matrix \newinstance. CImg get_invert(const bool use_LU=true) const { return CImg(*this,false).invert(use_LU); } - //! Compute the pseudo-inverse (Moore-Penrose) of the matrix. + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix. + /** + **/ CImg& pseudoinvert() { return get_pseudoinvert().move_to(*this); } + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance. CImg get_pseudoinvert() const { CImg 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!=0?1/s:(Tfloat)0; + 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 linear system AX=B where B=*this. + //! Solve a system of linear equations. + /** + \param A Matrix of the linear system. + \note Solve \c AX=B where \c B=*this. + **/ template CImg& solve(const CImg& A) { - if (_width!=1 || _depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1) + 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.", + "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) { + if (A._width==A._height) { // Classical linear system + if (_width!=1) { + CImg 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, one = 1, *const IPIV = new int[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_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.", + "solve(): LAPACK library function dgetrf_() returned error code %d.", cimg_instance, INFO); @@ -15702,7 +24909,7 @@ namespace cimg_library { cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); if (INFO) cimg::warn(_cimg_instance - "solve() : LAPACK library function dgetrs_() returned error code %d.", + "solve(): LAPACK library function dgetrs_() returned error code %d.", cimg_instance, INFO); } @@ -15715,10 +24922,44 @@ namespace cimg_library { lu._LU(indx,d); _solve(lu,indx); #endif - } else assign(A.get_pseudoinvert()*(*this)); + } else { // Least-square solution for non-square systems. +#ifdef cimg_use_lapack + if (_width!=1) { + CImg 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 CImg<_cimg_Ttfloat> get_solve(const CImg& A) const { return CImg<_cimg_Ttfloat>(*this,false).solve(A); @@ -15727,14 +24968,14 @@ namespace cimg_library { template CImg& _solve(const CImg& A, const CImg& indx) { typedef _cimg_Ttfloat Ttfloat; - const int N = size(); + const int N = (int)size(); int ii = -1; Ttfloat sum; for (int i = 0; i=0) for (int j = ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j); + 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; } @@ -15746,47 +24987,57 @@ namespace cimg_library { return *this; } - //! Solve a linear system AX=B where B=*this and A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ], - // stored as a 3 columns matrix (resolution uses the Thomas Algorithm). + //! 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 CImg& solve_tridiagonal(const CImg& A) { - const unsigned int siz = (int)size(); + const unsigned int siz = (unsigned int)size(); if (A._width!=3 || A._height!=siz) throw CImgArgumentException(_cimg_instance - "solve_tridiagonal() : Instance and tridiagonal matrix " + "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-4; + const Ttfloat epsilon = 1e-4f; CImg 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]; + 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)); + (*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 CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg& A) const { return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A); } - //! Compute the eigenvalues and eigenvectors of a matrix. + //! 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 const CImg& eigen(CImg& val, CImg &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.", + "eigen(): Instance is not a square matrix.", cimg_instance); - if (val.size()<_width) val.assign(1,_width); - if (vec.size()<_width*_width) vec.assign(_width,_width); + 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 : { @@ -15794,35 +25045,46 @@ namespace cimg_library { double f = e*e - 4*(a*d - b*c); if (f<0) cimg::warn(_cimg_instance - "CImg<%s>::eigen() : Complex eigenvalues found.", + "eigen(): Complex eigenvalues found.", cimg_instance); f = std::sqrt(f); - const double l1 = 0.5*(e-f), l2 = 0.5*(e+f); - const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b); + 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; - vec(0,0) = (t)std::cos(theta1); - vec(0,1) = (t)std::sin(theta1); - vec(1,0) = (t)std::cos(theta2); - vec(1,1) = (t)std::sin(theta2); + 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.", + "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 [val; vec], whose meaning is similar as in eigen(CImg&,CImg&) const. + **/ CImgList get_eigen() const { CImgList res(2); eigen(res[0],res[1]); return res; } - //! Compute the eigenvalues and eigenvectors of a symmetric matrix. + //! 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 const CImg& symmetric_eigen(CImg& val, CImg& vec) const { if (is_empty()) { val.assign(); vec.assign(); } @@ -15834,25 +25096,25 @@ namespace cimg_library { *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_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.", + "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]); + 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.", + "eigen(): Instance is not a square matrix.", cimg_instance); val.assign(1,_width); @@ -15863,7 +25125,10 @@ namespace cimg_library { return *this; } CImg V(_width,_width); - SVD(vec,val,V,false); + Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1.0f,cimg::abs(m),cimg::abs(M)); + (CImg(*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. @@ -15890,100 +25155,127 @@ namespace cimg_library { return *this; } + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \return A list of two images [val; vec], whose meaning are similar as in + symmetric_eigen(CImg&,CImg&) const. + **/ CImgList get_symmetric_eigen() const { CImgList res(2); symmetric_eigen(res[0],res[1]); return res; } - //! Sort values of a vector and get corresponding permutations. + //! 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 - CImg& sort(CImg& permutations, const bool increasing=true) { + CImg& sort(CImg& 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,increasing,true); + return _quicksort(0,size() - 1,permutations,is_increasing,true); } + //! Sort pixel values and get sorting permutations \newinstance. template - CImg get_sort(CImg& permutations, const bool increasing=true) const { - return (+*this).sort(permutations,increasing); + CImg get_sort(CImg& permutations, const bool is_increasing=true) const { + return (+*this).sort(permutations,is_increasing); } - //! Sort image values. - CImg& sort(const bool increasing=true, const char axis=0) { + //! 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& sort(const bool is_increasing=true, const char axis=0) { if (is_empty()) return *this; CImg perm; - switch (axis) { + switch (cimg::lowercase(axis)) { case 0 : - _quicksort(0,size()-1,perm,increasing,false); + _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,increasing); + get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing); CImg 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,increasing); + get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing); CImg 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,increasing); + get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing); CImg 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,increasing); + get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing); CImg 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' " + "sort(): Invalid specified axis '%c' " "(should be { x | y | z | c }).", cimg_instance,axis); } return *this; } - CImg get_sort(const bool increasing=true, const char axis=0) const { - return (+*this).sort(increasing,axis); + //! Sort pixel values \newinstance. + CImg get_sort(const bool is_increasing=true, const char axis=0) const { + return (+*this).sort(is_increasing,axis); } template - CImg& _quicksort(const int indm, const int indM, CImg& permutations, const bool increasing, const bool is_permutations) { + CImg& _quicksort(const long indm, const long indM, CImg& permutations, + const bool is_increasing, const bool is_permutations) { if (indm(*this)[mid]) { - cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[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]); + 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]); + 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]); + 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]); + 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]); + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); } } if (indM - indm>=3) { const T pivot = (*this)[mid]; - int i = indm, j = indM; - if (increasing) { + long i = indm, j = indM; + if (is_increasing) { do { while ((*this)[i]pivot) --j; @@ -16002,14 +25294,31 @@ namespace cimg_library { } } while (i<=j); } - if (indm A; // Input matrix (assumed to contain some values). + CImg<> U,S,V; + A.SVD(U,S,V) + \endcode + **/ template const CImg& SVD(CImg& U, CImg& S, CImg& V, const bool sorting=true, const unsigned int max_iteration=40, const float lambda=0) const { @@ -16017,7 +25326,7 @@ namespace cimg_library { else { U = *this; if (lambda!=0) { - const unsigned int delta = cimg::min(U._width,U._height); + const unsigned int delta = std::min(U._width,U._height); for (unsigned int i = 0; i=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g; for (int j = l; j=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g; for (int k = l; k=0; --i) { + for (int i = width() - 1; i>=0; --i) { if (i=0; --i) { - l = i+1; g = S[i]; + for (int i = std::min(width(),height()) - 1; i>=0; --i) { + l = i + 1; g = S[i]; for (int j = l; j=0; --k) { + for (int k = width() - 1; k>=0; --k) { for (unsigned int its = 0; its=1; --l) { - nm = l-1; - if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; } - if ((cimg::abs(S[nm])+anorm)==anorm) break; + 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 = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h; + 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; + 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))/(2*h*y); - g = (t)cimg::_pythagore(f,1.0); - f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x; + 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; + const int i = j + 1; g = rv1[i]; h = s*g; g = c*g; t y = S[i]; - t z = (t)cimg::_pythagore(f,h); - rv1[j] = z; c = f/z; s = h/z; - f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c; + 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 = (t)cimg::_pythagore(f,h); S[j] = z; - if (z) { z = 1/z; c = f*z; s = h*z; } - f = c*g+s*y; x = c*y-s*g; + 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; @@ -16152,6 +25462,11 @@ namespace cimg_library { return *this; } + //! Compute the SVD of the instance image, viewed as a general matrix. + /** + \return A list of three images [U; S; V], whose meaning is similar as in + SVD(CImg&,CImg&,CImg&,bool,unsigned int,float) const. + **/ CImgList get_SVD(const bool sorting=true, const unsigned int max_iteration=40, const float lambda=0) const { CImgList res(3); @@ -16159,7 +25474,7 @@ namespace cimg_library { return res; } - // INNER ROUTINE : Compute the LU decomposition of a permuted matrix (c.f. numerical recipies) + // [internal] Compute the LU decomposition of a permuted matrix. template CImg& _LU(CImg& indx, bool& d) { const int N = width(); @@ -16199,7 +25514,7 @@ namespace cimg_library { if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; if (j static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, const unsigned int starting_node, const unsigned int ending_node, - CImg& previous) { + CImg& 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.", + 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 dist(1,nb_nodes,1,1,cimg::type::max()); dist(starting_node) = 0; - previous.assign(1,nb_nodes,1,1,(t)-1); - previous(starting_node) = (t)starting_node; + previous_node.assign(1,nb_nodes,1,1,(t)-1); + previous_node(starting_node) = (t)starting_node; CImg Q(nb_nodes); - cimg_forX(Q,u) Q(u) = u; + cimg_forX(Q,u) Q(u) = (unsigned int)u; cimg::swap(Q(starting_node),Q(0)); unsigned int sizeQ = nb_nodes; while (sizeQ) { @@ -16243,9 +25561,10 @@ namespace cimg_library { const T alt = dmin + d; if (altdist(Q(left))) || (rightdist(Q(right)));) { + ((right=2*(pos + 1),(left=right - 1))dist(Q(left))) || + (rightdist(Q(right)));) { if (right - CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) { - return get_dijkstra(starting_node,ending_node,previous).move_to(*this); + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& 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 - CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) const { + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) const { if (_width!=_height || _depth!=1 || _spectrum!=1) throw CImgInstanceException(_cimg_instance - "dijkstra() : Instance is not a graph adjacency matrix.", + "dijkstra(): Instance is not a graph adjacency matrix.", cimg_instance); - return dijkstra(*this,_width,starting_node,ending_node,previous); + return dijkstra(*this,_width,starting_node,ending_node,previous_node); } //! Return minimal path in a graph, using the Dijkstra algorithm. @@ -16299,374 +25624,151 @@ namespace cimg_library { return get_dijkstra(starting_node,ending_node).move_to(*this); } + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { CImg foo; return get_dijkstra(starting_node,ending_node,foo); } - //! Return stream line of a 2d or 3d vector field. - CImg 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::_functor4d_streamline2d_oriented func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,0.0f); - } else { - typename CImg::_functor4d_streamline2d_directed func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,0.0f); - } - } - if (is_oriented_only) { - typename CImg::_functor4d_streamline3d_oriented func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f); - } - typename CImg::_functor4d_streamline3d_directed func(*this); - return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f); - } - - //! Return stream line of a 3d vector field. + //! Return an image containing the ascii codes of the specified string. /** - \param interpolation_type Type of interpolation (can be 0=nearest int, 1=linear, 2=2nd-order RK, 3=4th-order RK. - - **/ - template - static CImg 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 && (xx1 || yy1 || zz1))) return CImg(); - const unsigned int size_L = (unsigned int)cimg::round(L/dl+1); - CImg 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 && (Xx1 || Yy1 || Zz1)) 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 && (Xx1 || Yy1 || Zz1)) 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 && (Xx1 || Yy1 || Zz1)) 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 && (Xx1 || Yy1 || Zz1)) 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 vector field. - static CImg 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& ref; - _functor4d_streamline2d_directed(const CImg& 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& ref; - _functor4d_streamline3d_directed(const CImg& 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& ref; - CImg *pI; - _functor4d_streamline2d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(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& 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& ref; - CImg *pI; - _functor4d_streamline3d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(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& 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,yi,zi,2); - I(0,0,0,1) = (float)ref(xi,yi,nzi,0); I(0,0,0,1) = (float)ref(xi,yi,nzi,1); I(0,0,0,2) = (float)ref(xi,yi,nzi,2); - I(1,0,0,1) = (float)ref(nxi,yi,nzi,0); I(1,0,0,1) = (float)ref(nxi,yi,nzi,1); I(1,0,0,2) = (float)ref(nxi,yi,nzi,2); - I(1,1,0,1) = (float)ref(nxi,nyi,nzi,0); I(1,1,0,1) = (float)ref(nxi,nyi,nzi,1); I(1,1,0,2) = (float)ref(nxi,nyi,nzi,2); - I(0,1,0,1) = (float)ref(xi,nyi,nzi,0); I(0,1,0,1) = (float)ref(xi,nyi,nzi,1); I(0,1,0,2) = (float)ref(xi,yi,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() { delete mp; } - _functor4d_streamline_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,"streamline"); } - float operator()(const float x, const float y, const float z, const unsigned int c) const { - return (float)mp->eval(x,y,z,c); - } - }; - - //! Return an image containing the specified string. - static CImg string(const char *const str, const bool include_last_zero=true) { + \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 string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) { if (!str) return CImg(); - return CImg(str,std::strlen(str)+(include_last_zero?1:0)); + return CImg(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared); } - //! Return a vector with specified coefficients. + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ static CImg vector(const T& a0) { - _cimg_static CImg r(1,1); + CImg r(1,1); r[0] = a0; return r; } - //! Return a vector with specified coefficients. + //! Return a \c 1x2 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ static CImg vector(const T& a0, const T& a1) { - _cimg_static CImg r(1,2); T *ptr = r._data; + CImg r(1,2); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; return r; } - //! Return a vector with specified coefficients. + //! 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 vector(const T& a0, const T& a1, const T& a2) { - _cimg_static CImg r(1,3); T *ptr = r._data; + CImg r(1,3); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; return r; } - //! Return a vector with specified coefficients. + //! 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 vector(const T& a0, const T& a1, const T& a2, const T& a3) { - _cimg_static CImg r(1,4); T *ptr = r._data; + CImg r(1,4); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; return r; } - //! Return a vector with specified coefficients. + //! Return a \c 1x5 image containing specified values. static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { - _cimg_static CImg r(1,5); T *ptr = r._data; + CImg r(1,5); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; return r; } - //! Return a vector with specified coefficients. + //! Return a \c 1x6 image containing specified values. static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { - _cimg_static CImg r(1,6); T *ptr = r._data; + CImg r(1,6); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; return r; } - //! Return a vector with specified coefficients. + //! Return a \c 1x7 image containing specified values. static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) { - _cimg_static CImg r(1,7); T *ptr = r._data; + CImg 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 vector with specified coefficients. + //! Return a \c 1x8 image containing specified values. static CImg 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_static CImg r(1,8); T *ptr = r._data; + CImg 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 vector with specified coefficients. + //! Return a \c 1x9 image containing specified values. static CImg 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_static CImg r(1,9); T *ptr = r._data; + CImg 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 vector with specified coefficients. + //! Return a \c 1x10 image containing specified values. static CImg 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_static CImg r(1,10); T *ptr = r._data; + CImg 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 vector with specified coefficients. + //! Return a \c 1x11 image containing specified values. static CImg 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_static CImg r(1,11); T *ptr = r._data; + CImg 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 vector with specified coefficients. + //! Return a \c 1x12 image containing specified values. static CImg 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_static CImg r(1,12); T *ptr = r._data; + CImg 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 vector with specified coefficients. + //! Return a \c 1x13 image containing specified values. static CImg 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_static CImg r(1,13); T *ptr = r._data; + CImg 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; @@ -16674,12 +25776,12 @@ namespace cimg_library { return r; } - //! Return a vector with specified coefficients. + //! Return a \c 1x14 image containing specified values. static CImg 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_static CImg r(1,14); T *ptr = r._data; + CImg 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; @@ -16687,12 +25789,12 @@ namespace cimg_library { return r; } - //! Return a vector with specified coefficients. + //! Return a \c 1x15 image containing specified values. static CImg 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_static CImg r(1,15); T *ptr = r._data; + CImg 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; @@ -16700,12 +25802,12 @@ namespace cimg_library { return r; } - //! Return a vector with specified coefficients. + //! Return a \c 1x16 image containing specified values. static CImg 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_static CImg r(1,16); T *ptr = r._data; + CImg 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; @@ -16713,37 +25815,58 @@ namespace cimg_library { return r; } - //! Return a 1x1 square matrix with specified coefficients. + //! Return a 1x1 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ static CImg matrix(const T& a0) { return vector(a0); } - //! Return a 2x2 square matrix with specified coefficients. + //! 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 matrix(const T& a0, const T& a1, const T& a2, const T& a3) { - _cimg_static CImg r(2,2); T *ptr = r._data; + CImg r(2,2); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; return r; } - //! Return a 3x3 square matrix with specified coefficients. + //! 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 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_static CImg r(3,3); T *ptr = r._data; + CImg 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 square matrix with specified coefficients. + //! Return a 4x4 matrix containing specified coefficients. static CImg 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_static CImg r(4,4); T *ptr = r._data; + CImg 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; @@ -16751,13 +25874,13 @@ namespace cimg_library { return r; } - //! Return a 5x5 square matrix with specified coefficients. + //! Return a 5x5 matrix containing specified coefficients. static CImg 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_static CImg r(5,5); T *ptr = r._data; + CImg 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; @@ -16766,47 +25889,54 @@ namespace cimg_library { return r; } - //! Return a 1x1 symmetric matrix with specified coefficients. - static CImg tensor(const T& a1) { - return matrix(a1); + //! Return a 1x1 symmetric matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg tensor(const T& a0) { + return matrix(a0); } - //! Return a 2x2 symmetric matrix tensor with specified coefficients. - static CImg tensor(const T& a1, const T& a2, const T& a3) { - return matrix(a1,a2,a2,a3); + //! Return a 2x2 symmetric matrix tensor containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2) { + return matrix(a0,a1,a1,a2); } - //! Return a 3x3 symmetric matrix with specified coefficients. - static CImg tensor(const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) { - return matrix(a1,a2,a3,a2,a4,a5,a3,a5,a6); + //! Return a 3x3 symmetric matrix containing specified coefficients. + static CImg 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 with specified coefficients. + //! Return a 1x1 diagonal matrix containing specified coefficients. static CImg diagonal(const T& a0) { return matrix(a0); } - //! Return a 2x2 diagonal matrix with specified coefficients. + //! Return a 2x2 diagonal matrix containing specified coefficients. static CImg diagonal(const T& a0, const T& a1) { return matrix(a0,0,0,a1); } - //! Return a 3x3 diagonal matrix with specified coefficients. + //! Return a 3x3 diagonal matrix containing specified coefficients. static CImg 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 with specified coefficients. + //! Return a 4x4 diagonal matrix containing specified coefficients. static CImg 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 with specified coefficients. + //! Return a 5x5 diagonal matrix containing specified coefficients. static CImg 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 identity_matrix(const unsigned int N) { CImg res(N,N,1,1,0); cimg_forX(res,x) res(x,x) = 1; @@ -16814,35 +25944,42 @@ namespace cimg_library { } //! Return a N-numbered sequence vector from \p a0 to \p a1. - static CImg sequence(const unsigned int N, const T a0, const T a1) { + /** + \param N Size of the resulting vector. + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + static CImg sequence(const unsigned int N, const T& a0, const T& a1) { if (N) return CImg(1,N).sequence(a0,a1); return CImg(); } - //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w. - static CImg rotation_matrix(const float x, const float y, const float z, const float w, const bool quaternion_data=false) { - float X,Y,Z,W; - if (!quaternion_data) { - const float norm = (float)std::sqrt(x*x + y*y + z*z), - nx = norm>0?x/norm:0, - ny = norm>0?y/norm:0, - nz = norm>0?z/norm:1, - nw = norm>0?w:0, - sina = (float)std::sin(nw/2), - cosa = (float)std::cos(nw/2); - X = nx*sina; - Y = ny*sina; - Z = nz*sina; - W = cosa; - } else { - const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w); - if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; } + //! 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 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::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)); } - const float xx = X*X, xy = X*Y, xz = X*Z, xw = X*W, yy = Y*Y, yz = Y*Z, yw = Y*W, zz = Z*Z, zw = Z*W; - return CImg::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)), (T)(2*(xz-yw)), - (T)(2*(xy-zw)), (T)(1-2*(xx+zz)), (T)(2*(yz+xw)), - (T)(2*(xz+yw)), (T)(2*(yz-xw)), (T)(1-2*(xx+yy))); + 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::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)); } //@} @@ -16852,39 +25989,44 @@ namespace cimg_library { //@{ //----------------------------------- - //! Fill an image by a value \p val. + //! Fill all pixel values with specified value. /** - \param val = fill value - \note All pixel values of the image instance will be initialized by \p val. + \param val Fill value. **/ - CImg& fill(const T val) { + CImg& 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)val,size()*sizeof(T)); + else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*) return *this; } - CImg get_fill(const T val) const { + //! Fill all pixel values with specified value \newinstance. + CImg get_fill(const T& val) const { return CImg(_width,_height,_depth,_spectrum).fill(val); } - //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively. - CImg& fill(const T val0, const T val1) { + //! Fill sequentially all pixel values with specified values. + /** + \param val0 First fill value. + \param val1 Second fill value. + **/ + CImg& fill(const T& val0, const T& val1) { if (is_empty()) return *this; - T *ptrd, *ptre = end()-1; + T *ptrd, *ptre = end() - 1; for (ptrd = _data; ptrd get_fill(const T val0, const T val1) const { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg get_fill(const T& val0, const T& val1) const { return CImg(_width,_height,_depth,_spectrum).fill(val0,val1); } - //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2. - CImg& fill(const T val0, const T val1, const T val2) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2) { if (is_empty()) return *this; - T *ptrd, *ptre = end()-2; + T *ptrd, *ptre = end() - 2; for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2) const { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg get_fill(const T& val0, const T& val1, const T& val2) const { return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2); } - //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3. - CImg& fill(const T val0, const T val1, const T val2, const T val3) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3) { if (is_empty()) return *this; - T *ptrd, *ptre = end()-3; + T *ptrd, *ptre = end() - 3; for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3) const { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const { return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3); } - //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4. - CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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 get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const { return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4); } - //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4 and \a val5. - CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 5; for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const { return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5); } - //! Fill sequentially pixel values. - CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 6; for (ptrd = _data; ptrd get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 7; for (ptrd = _data; ptrd 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 { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 8; for (ptrd = _data; ptrd 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 { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 9; for (ptrd = _data; ptrd 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 { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 10; for (ptrd = _data; ptrd 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 { + //! Fill sequentially all pixel values with specified values \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 11; for (ptrd = _data; ptrd 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(_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 \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 12; for (ptrd = _data; ptrd 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(_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 \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 13; for (ptrd = _data; ptrd 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(_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 \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 14; for (ptrd = _data; ptrd 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(_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 \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14); } - //! Fill sequentially pixel values. - CImg& 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) { + //! Fill sequentially all pixel values with specified values \overloading. + CImg& 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; + T *ptrd, *ptre = end() - 15; for (ptrd = _data; ptrd 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, - val13,val14,val15); + //! Fill sequentially all pixel values with specified values \newinstance. + CImg 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(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14,val15); } - //! Fill image values according to the given expression, which can be a formula or a list of values. - CImg& fill(const char *const expression, const bool repeat_flag) { + //! 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& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _fill(expression,repeat_values,allow_formula,list_inputs,list_outputs,"fill",0); + } + + CImg& _fill(const char *const expression, const bool repeat_values, bool allow_formula, + const CImgList *const list_inputs, CImgList *const list_outputs, + const char *const calling_function, const CImg *provides_copy) { if (is_empty() || !expression || !*expression) return *this; const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; - try { // Try to fill values according to a formula. - const CImg _base = std::strstr(expression,"i(")?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression,"fill"); - T *ptrd = _data; - cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp.eval((double)x,(double)y,(double)z,(double)c); - } catch (CImgException& e) { // If failed, try to recognize a list of values. - char item[16384] = { 0 }, sep = 0; + cimg::exception_mode(0); + CImg is_error; + bool is_value_sequence = false; + + if (allow_formula) { + + // 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; + if (!is_value_sequence) try { + CImg 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); // Needs input copy + + bool do_in_parallel = false; +#ifdef cimg_use_openmp + cimg_openmp_if(*expression=='*' || *expression==':' || + (mp.is_parallelizable && _width>=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 res(1,mp.result_dim); + cimg_rofYZ(*this,y,z) { + cimg_abort_test(); + 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 res(1,mp.result_dim); + cimg_forYZ(*this,y,z) { + cimg_abort_test(); + 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 { + cimg_abort_test(); + CImg 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() cimg_abort_catch_fill() + } +#endif + } + + } else { // Scalar-valued expression + T *ptrd = *expression=='<'?end() - 1:_data; + if (*expression=='<') + 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) + 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 { + cimg_abort_test(); + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); + } cimg_abort_catch() cimg_abort_catch_fill() + } +#endif + } + } + mp.end(); + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } + } + + // Try to fill values according to a value sequence. + if (!allow_formula || is_value_sequence || is_error) { + CImg item(256); + char sep = 0; const char *nexpression = expression; - unsigned int nb = 0; const unsigned int siz = size(); + ulongT nb = 0; + const ulongT siz = size(); T *ptrd = _data; for (double val = 0; *nexpression && nb0 && std::sscanf(item,"%lf",&val)==1) { - nexpression+=std::strlen(item) + (err>1?1: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 get_fill(const char *const values, const bool repeat_values) const { - return (+*this).fill(values,repeat_values); + //! Fill sequentially pixel values according to a given expression \newinstance. + CImg get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return (+*this).fill(expression,repeat_values,allow_formula,list_inputs,list_outputs); } - //! Fill image values according to the values found in the specified image. + //! 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 CImg& fill(const CImg& 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 CImg get_fill(const CImg& values, const bool repeat_values=true) const { - return repeat_values?CImg(_width,_height,_depth,_spectrum).fill(values,repeat_values):(+*this).fill(values,repeat_values); + return repeat_values?CImg(_width,_height,_depth,_spectrum).fill(values,repeat_values): + (+*this).fill(values,repeat_values); } - //! Fill image values along the X-axis at the specified pixel position (y,z,c). + //! 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& 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; \ @@ -17335,133 +26641,264 @@ namespace cimg_library { return *this; } + //! Fill pixel values along the X-axis at a specified pixel position \overloading. CImg& 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 image values along the Y-axis at the specified pixel position (x,z,c). + //! 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& 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& 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 image values along the Z-axis at the specified pixel position (x,y,c). + //! 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& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) { - const unsigned int wh = _width*_height; + 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& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) { - const unsigned int wh = _width*_height; + 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 image values along the C-axis at the specified pixel position (x,y,z). + //! 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& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { - const unsigned int whd = _width*_height*_depth; + 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& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { - const unsigned int whd = _width*_height*_depth; + 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; } - //! Remove specified value from the image buffer, and return resulting buffer as a one-column vector. - CImg& discard(const T value) { - return get_discard(value).move_to(*this); - } - - CImg get_discard(const T value) const { - CImg res(1,size()); - T *pd = res._data; - for (const T *ps = _data, *const pse = end(); ps(); - return res.resize(1,pd-res._data,1,1,-1); - } - - //! Remove specified values sequence from the image buffer, and return resulting buffer as a one-column vector. + //! 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 - CImg& discard(const CImg& values) { - return get_discard(values).move_to(*this); + CImg& discard(const CImg& values, const char axis=0) { + if (is_empty() || !values) return *this; + return get_discard(values,axis).move_to(*this); } template - CImg get_discard(const CImg& values) const { - if (!values) return *this; - if (values.size()==1) return get_discard(*values); - CImg res(1,size()); - T *pd = res._data; - const t *const pve = values.end(); - for (const T *ps = _data, *const pse = end(); ps get_discard(const CImg& values, const char axis=0) const { + CImg 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; } } } - ps = _ps; + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = (int)i + 1; }} + } + const ulongT siz = size(); + if ((ulongT)i0(); - return res.resize(1,pd-res._data,1,1,-1); + } + return res; } - //! Invert endianness of the image buffer. + //! Discard neighboring duplicates in the image buffer, along the specified axis. + CImg& 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 get_discard(const char axis=0) const { + CImg 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& invert_endianness() { cimg::invert_endianness(_data,size()); return *this; } + //! Invert endianness of all pixel values \newinstance. CImg get_invert_endianness() const { return (+*this).invert_endianness(); } - //! Fill the image instance with random values between specified range. - CImg& rand(const T val_min, const T val_max) { - const float delta = (float)val_max - (float)val_min; - cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta); + //! 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& rand(const T& val_min, const T& val_max) { + const float delta = (float)val_max - (float)val_min + (cimg::type::is_float()?0:1); + if (cimg::type::is_float()) cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta); + else cimg_for(*this,ptrd,T) *ptrd = std::min(val_max,(T)(val_min + cimg::rand()*delta)); return *this; } - CImg get_rand(const T val_min, const T val_max) const { + //! Fill image with random values in specified range \newinstance. + CImg get_rand(const T& val_min, const T& val_max) const { return (+*this).rand(val_min,val_max); } - //! Compute image with rounded pixel values. + //! Round pixel values. /** \param y Rounding precision. - \param rounding_type Roundin type, can be 0 (nearest), 1 (forward), -1(backward). + \param rounding_type Rounding type. Can be: + - \c -1: Backward. + - \c 0: Nearest. + - \c 1: Forward. **/ CImg& round(const double y=1, const int rounding_type=0) { - if (y>0) cimg_for(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type); + if (y>0) + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192)) + cimg_rof(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type); return *this; } + //! Round pixel values \newinstance. CImg get_round(const double y=1, const unsigned int rounding_type=0) const { return (+*this).round(y,rounding_type); } - //! Add random noise to the values of the image instance. + //! 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). + \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. + - 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::get_noise() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \par Example \code const CImg img("reference.jpg"), res = img.get_noise(40); (img,res.normalize(0,255)).display(); @@ -17469,101 +26906,94 @@ namespace cimg_library { \image html ref_noise.jpg **/ CImg& noise(const double sigma, const unsigned int noise_type=0) { - if (!is_empty()) { - const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::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.0); - switch (noise_type) { - case 0 : { // Gaussian noise - cimg_for(*this,ptrd,T) { - Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand()); - if (val>vmax) val = vmax; - if (valvmax) val = vmax; - if (val::is_float()?1:cimg::type::max()); } - cimg_for(*this,ptrd,T) if (cimg::rand()*100vmax) val = vmax; - if (val::min(), vmax = (Tfloat)cimg::type::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.0); + switch (noise_type) { + case 0 : { // Gaussian noise + cimg_rof(*this,ptrd,T) { + Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand()); + if (val>vmax) val = vmax; + if (valvmax) val = vmax; + if (val::is_float()?(Tfloat)1:(Tfloat)cimg::type::max(); } + cimg_rof(*this,ptrd,T) if (cimg::rand(100)vmax) val = vmax; + if (val get_noise(const double sigma, const unsigned int noise_type=0) const { return (+*this).noise(sigma,noise_type); } - //! Linearly normalize values of the image instance between \p value_min and \p value_max. + //! Linearly normalize pixel values. /** - \param value_min Minimum desired value of the resulting image. - \param value_max Maximum desired value of the resulting image. - \return A reference to the modified image instance. - \note - - Function \p CImg::get_normalize() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \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 img("reference.jpg"), res = img.get_normalize(160,220); (img,res).display(); \endcode \image html ref_normalize2.jpg **/ - CImg& normalize(const T value_min, const T value_max) { + CImg& normalize(const T& min_value, const T& max_value) { if (is_empty()) return *this; - const T a = value_min get_normalize(const T value_min, const T value_max) const { - return CImg(*this,false).normalize((Tfloat)value_min,(Tfloat)value_max); + //! Linearly normalize pixel values \newinstance. + CImg get_normalize(const T& min_value, const T& max_value) const { + return CImg(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value); } //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm. /** - \return A reference to the modified image instance. - \note - - Function \p CImg::get_normalize() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \par Example \code const CImg img("reference.jpg"), res = img.get_normalize(); (img,res.normalize(0,255)).display(); @@ -17571,31 +27001,32 @@ namespace cimg_library { \image html ref_normalize.jpg **/ CImg& normalize() { - T *ptrd = _data; - const unsigned int whd = _width*_height*_depth; - cimg_forXYZ(*this,x,y,z) { - 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; } + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=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 get_normalize() const { return CImg(*this,false).normalize(); } - //! Compute L2-norm of each multi-valued pixel of the image instance. + //! Compute Lp-norm of each multi-valued pixel of the image instance. /** - \param norm_type Type of computed vector norm (can be \p 0=Linf, \p 1=L1 or \p 2=L2). - \return A reference to the modified image instance. - \note - - Function \p CImg::get_norm() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \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 img("reference.jpg"), res = img.get_norm(); (img,res.normalize(0,255)).display(); @@ -17603,79 +27034,120 @@ namespace cimg_library { \image html ref_norm.jpg **/ CImg& norm(const int norm_type=2) { - if (_spectrum==1) return abs(); + 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 get_norm(const int norm_type=2) const { if (is_empty()) return *this; - if (_spectrum==1) return get_abs(); - const T *ptrs = _data; - const unsigned int whd = _width*_height*_depth; + if (_spectrum==1 && norm_type) return get_abs(); + const ulongT whd = (ulongT)_width*_height*_depth; CImg res(_width,_height,_depth); - Tfloat *ptrd = res._data; switch (norm_type) { - case -1 : { // Linf norm - cimg_forXYZ(*this,x,y,z) { - 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; + case -1 : { // Linf-norm. + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=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 1 : { // L1 norm - cimg_forXYZ(*this,x,y,z) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; } - *(ptrd++) = n; + case 0 : { // L0-norm. + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=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; - default : { // L2 norm - cimg_forXYZ(*this,x,y,z) { - Tfloat n = 0; - const T *_ptrs = ptrs++; - cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; } - *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); + case 1 : { // L1-norm. + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=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>=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>=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 values of the image instance between \p value_min and \p value_max. + //! Cut pixel values in specified range. /** - \param value_min Minimum desired value of the resulting image. - \param value_max Maximum desired value of the resulting image. - \return A reference to the modified image instance. - \note - - Function \p CImg::get_cut() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \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 img("reference.jpg"), res = img.get_cut(160,220); (img,res).display(); \endcode \image html ref_cut.jpg **/ - CImg& cut(const T value_min, const T value_max) { + CImg& cut(const T& min_value, const T& max_value) { if (is_empty()) return *this; - const T a = value_minb)?b:*ptrd); + const T a = min_value=32768)) + cimg_rof(*this,ptrd,T) *ptrd = (*ptrdb)?b:*ptrd); return *this; } - CImg get_cut(const T value_min, const T value_max) const { - return (+*this).cut(value_min,value_max); + //! Cut pixel values in specified range \newinstance. + CImg get_cut(const T& min_value, const T& max_value) const { + return (+*this).cut(min_value,max_value); } - //! Uniformly quantize values of the image instance into \p nb_levels levels. + //! 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. - \return A reference to the modified image instance. - \note - - Function \p CImg::get_quantize() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \par Example \code const CImg img("reference.jpg"), res = img.get_quantize(4); (img,res).display(); @@ -17685,196 +27157,234 @@ namespace cimg_library { CImg& 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.", + "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_for(*this,ptrd,T) { - const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); - *ptrd = (T)(m + cimg::min(val,nb_levels-1)*range/nb_levels); - } else cimg_for(*this,ptrd,T) { - const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); - *ptrd = (T)cimg::min(val,nb_levels-1); - } + if (keep_range) + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { + const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); + *ptrd = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels); + } else + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { + const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); + *ptrd = (T)std::min(val,nb_levels - 1); + } } return *this; } + //! Uniformly quantize pixel values \newinstance. CImg get_quantize(const unsigned int n, const bool keep_range=true) const { return (+*this).quantize(n,keep_range); } - //! Threshold values of the image instance. + //! 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. - \return A reference to the modified image instance. Resulting pixel values are either equal to 0 or 1. - \note - - Function \p CImg::get_threshold() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \par Example \code const CImg img("reference.jpg"), res = img.get_threshold(128); (img,res.normalize(0,255)).display(); \endcode \image html ref_threshold.jpg **/ - CImg& threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) { + CImg& 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_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v+value):(T)0; } - else cimg_for(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0; + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { + const T v = *ptrd; + *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536)) + cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0; } else { - if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v+value):(T)0; } - else cimg_for(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0; + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768)) + cimg_rof(*this,ptrd,T) { + const T v = *ptrd; + *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536)) + cimg_rof(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0; } return *this; } - CImg get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const { + //! Threshold pixel values \newinstance. + CImg 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 the image instance. + //! Compute the histogram of pixel values. /** \param nb_levels Number of desired histogram levels. - \param value_min Minimum pixel value considered for the histogram computation. All pixel values lower than \p value_min will not be counted. - \param value_max Maximum pixel value considered for the histogram computation. All pixel values higher than \p value_max will not be counted. - \return image instance is replaced by its histogram, defined as a \p CImg(nb_levels) image. + \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. - - If \p value_min==value_max==0 (default behavior), the function first estimates the whole range of pixel values - then uses it to compute the histogram. + - 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. - - Function \p CImg::get_histogram() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \par Example \code const CImg img = CImg("reference.jpg").histogram(256); img.display_graph(0,3); \endcode \image html ref_histogram.jpg **/ - CImg& histogram(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) { - return get_histogram(nb_levels,value_min,value_max).move_to(*this); + CImg& 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); } - CImg get_histogram(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) const { - if (!nb_levels) - throw CImgArgumentException(_cimg_instance - "histogram() : Invalid histogram request with 0 levels.", - cimg_instance); + //! Compute the histogram of pixel values \overloading. + CImg& histogram(const unsigned int nb_levels) { + return get_histogram(nb_levels).move_to(*this); + } - if (is_empty()) return CImg(); - T vmin = value_min, vmax = value_max; - CImg res(nb_levels,1,1,1,0); - if (vmin>=vmax && vmin==0) vmin = min_max(vmax); - if (vmin get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const { + if (!nb_levels || is_empty()) return CImg(); + const double + vmin = (double)(min_value 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:(int)((val-vmin)*nb_levels/(vmax-vmin))]; - } else res[0]+=size(); + if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))]; + } return res; } - //! Compute the histogram-equalized version of the image instance. + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels) const { + if (!nb_levels || is_empty()) return CImg(); + 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 value_min Minimum pixel value considered for the histogram computation. All pixel values lower than \p value_min will not be counted. - \param value_max Maximum pixel value considered for the histogram computation. All pixel values higher than \p value_max will not be counted. - \return A reference to the modified image instance. - \note - - If \p value_min==value_max==0 (default behavior), the function first estimates the whole range of pixel values - then uses it to equalize the histogram. - - Function \p CImg::get_equalize() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \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 img("reference.jpg"), res = img.get_equalize(256); (img,res).display(); \endcode \image html ref_equalize.jpg **/ - CImg& equalize(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) { - if (is_empty()) return *this; - T vmin = value_min, vmax = value_max; - if (vmin==vmax && vmin==0) vmin = min_max(vmax); - if (vmin hist = get_histogram(nb_levels,vmin,vmax); - float cumul = 0; - cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } - cimg_for(*this,ptrd,T) { - const int pos = (unsigned int)((*ptrd-vmin)*(nb_levels-1)/(vmax-vmin)); - if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/size()); - } + CImg& 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 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()>=1048576)) + cimg_rof(*this,ptrd,T) { + const int pos = (int)((*ptrd-vmin)*(nb_levels - 1.)/(vmax-vmin)); + if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/cumul); } return *this; } - CImg get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const { + //! Equalize histogram of pixel values \overloading. + CImg& 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 get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const { return (+*this).equalize(nblevels,val_min,val_max); } - //! Index multi-valued pixels of the image instance, regarding to a predefined palette. + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels) const { + return (+*this).equalize(nblevels); + } + + //! Index multi-valued pixels regarding to a specified colormap. /** - \param palette Multi-valued palette used as the basis for multi-valued pixel indexing. - \param dithering Tells if Floyd-Steinberg dithering is activated or not. - \param map_indexes Tell if the values of the resulting image are the palette indices or the palette vectors. - \return A reference to the modified image instance. + \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(palette,dithering,1) is equivalent to img.index(palette,dithering,0).map(palette). - - Function \p CImg::get_index() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + - \p img.index(colormap,dithering,1) is equivalent to img.index(colormap,dithering,0).map(colormap). + \par Example \code - const CImg img("reference.jpg"), palette(3,1,1,3, 0,128,255, 0,128,255, 0,128,255); - const CImg res = img.get_index(palette,true,true); + const CImg img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255); + const CImg res = img.get_index(colormap,1,true); (img,res).display(); \endcode \image html ref_index.jpg **/ template - CImg& index(const CImg& palette, const bool dithering=false, const bool map_indexes=false) { - return get_index(palette,dithering,map_indexes).move_to(*this); + CImg& index(const CImg& 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 CImg::Tuint> - get_index(const CImg& palette, const bool dithering=false, const bool map_indexes=true) const { - if (palette._spectrum!=_spectrum) + get_index(const CImg& colormap, const float dithering=1, const bool map_indexes=true) const { + if (colormap._spectrum!=_spectrum) throw CImgArgumentException(_cimg_instance - "index() : Instance and specified palette (%u,%u,%u,%u,%p) " + "index(): Instance and specified colormap (%u,%u,%u,%u,%p) " "have incompatible dimensions.", cimg_instance, - palette._width,palette._height,palette._depth,palette._spectrum,palette._data); + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); typedef typename CImg::Tuint tuint; if (is_empty()) return CImg(); - const unsigned int whd = _width*_height*_depth, pwhd = palette._width*palette._height*palette._depth; + const ulongT + whd = (ulongT)_width*_height*_depth, + pwhd = (ulongT)colormap._width*colormap._height*colormap._depth; CImg res(_width,_height,_depth,map_indexes?_spectrum:1); tuint *ptrd = res._data; - if (dithering) { // Dithered versions. + 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 cache = get_crop(-1,0,0,0,_width,1,0,_spectrum-1); + CImg 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 unsigned int cwhd = cache._width*cache._height*cache._depth; + const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth; switch (_spectrum) { case 1 : { // Optimized for scalars. cimg_forYZ(*this,y,z) { - if (yvalM?valM:_val0; - Tfloat distmin = cimg::type::max(); const t *ptrmin0 = palette._data; - for (const t *ptrp0 = palette._data, *ptrp_end = ptrp0 + pwhd; ptrp0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0valM?valM:_val0, _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1; - Tfloat distmin = cimg::type::max(); const t *ptrmin0 = palette._data; - for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0valM?valM:_val0, _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1, _val2 = (Tfloat)*ptrs2, val2 = _val2valM?valM:_val2; - Tfloat distmin = cimg::type::max(); const t *ptrmin0 = palette._data; - for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin = palette._data; - for (const t *ptrp = palette._data, *ptrp_end = ptrp + pwhd; ptrp::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrpvalM?valM:_val; @@ -17974,17 +27492,17 @@ namespace cimg_library { } if (dist::max(); const t *ptrmin0 = palette._data; - for (const t *ptrp0 = palette._data, *ptrp_end = ptrp0 + pwhd; ptrp0=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::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0::max(); const t *ptrmin0 = palette._data; - for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0=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::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin0 = palette._data; - for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0=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::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin = palette._data; - for (const t *ptrp = palette._data, *ptrp_end = ptrp + pwhd; ptrp=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::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp::get_map() is also defined. It returns a non-shared modified copy of the image instance. - \par Sample code : + \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 img("reference.jpg"), - palette1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), - palette2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), - res = img.get_index(palette1,false).map(palette2); + 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 - CImg& map(const CImg& palette) { - return get_map(palette).move_to(*this); + CImg& map(const CImg& 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 - CImg get_map(const CImg& palette) const { - if (_spectrum!=1 && palette._spectrum!=1) + CImg get_map(const CImg& colormap, const unsigned int boundary_conditions=0) const { + if (_spectrum!=1 && colormap._spectrum!=1) throw CImgArgumentException(_cimg_instance - "map() : Instance and specified palette (%u,%u,%u,%u,%p) " + "map(): Instance and specified colormap (%u,%u,%u,%u,%p) " "have incompatible dimensions.", cimg_instance, - palette._width,palette._height,palette._depth,palette._spectrum,palette._data); + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); - const unsigned int whd = _width*_height*_depth, pwhd = palette._width*palette._height*palette._depth; - CImg res(_width,_height,_depth,palette._spectrum==1?_spectrum:palette._spectrum); - switch (palette._spectrum) { - case 1 : { // Optimized for scalars. - const T *ptrs = _data + whd*_spectrum; - cimg_for(res,ptrd,t) { - const unsigned int _ind = (unsigned int)*(--ptrs), ind = _ind 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& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) { return get_label(is_high_connectivity,tolerance).move_to(*this); } - CImg get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0) const { - if (is_empty()) return CImg(); + //! Label connected components \newinstance. + CImg get_label(const bool is_high_connectivity=false, + const Tfloat tolerance=0) const { + if (is_empty()) return CImg(); // Create neighborhood tables. - int dx[26], dy[26], dz[26], nb = 0; - if (_depth>1) { // 3d version. - for (unsigned int _dx = 0; _dx<=1; ++_dx) - for (unsigned int _dy = 0; _dy<=1; ++_dy) - for (unsigned int _dz = 0; _dz<=1; ++_dz) - if (_dx+_dy+_dz && (is_high_connectivity || _dx+_dy+_dz==1)) { dx[nb] = (int)_dx; dy[nb] = (int)_dy; dz[nb] = (int)_dz; ++nb; } - } else { // 2d version. - for (unsigned int _dx = 0; _dx<=1; ++_dx) - for (unsigned int _dy = 0; _dy<=1; ++_dy) - if (_dx+_dy && (is_high_connectivity || _dx+_dy==1)) { dx[nb] = (int)_dx; dy[nb] = (int)_dy; dz[nb] = 0; ++nb; } + 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; } - return _get_label(nb,dx,dy,dz,tolerance); + 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 CImg& label(const CImg& connectivity_mask, const Tfloat tolerance=0) { return get_label(connectivity_mask,tolerance).move_to(*this); } + //! Label connected components \newinstance. template - CImg get_label(const CImg& connectivity_mask, const Tfloat tolerance=0) const { + CImg get_label(const CImg& connectivity_mask, + const Tfloat tolerance=0) const { int nb = 0; cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb; CImg 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)) { + 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 _get_label(nb,dx,dy,dz,tolerance); + return _label(nb,dx,dy,dz,tolerance); } - // Generic version, allows any kind of neighbor connectivity. Use it at your own risk :) - CImg _get_label(const unsigned int nb, const int *const dx, const int *const dy, const int *const dz, - const Tfloat tolerance) const { - CImg res(_width,_height,_depth,_spectrum); + CImg _label(const unsigned int nb, const int + *const dx, const int *const dy, const int *const dz, + const Tfloat tolerance) const { + CImg res(_width,_height,_depth,_spectrum); cimg_forC(*this,c) { - CImg _res = res.get_shared_channel(c); + CImg _res = res.get_shared_channel(c); // Init label numbers. - unsigned long *ptr = _res.data(); + ulongT *ptr = _res.data(); cimg_foroff(_res,p) *(ptr++) = p; // For each neighbour-direction, label. for (unsigned int n = 0; n& _system_strescape() { +#define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\ + move_to(list); \ + CImg(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break + CImgList 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(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list); + return (list>'x').move_to(*this); + } + //@} //--------------------------------- // @@ -18224,34 +27912,50 @@ namespace cimg_library { //@{ //--------------------------------- - //! Return a palette 'default' with 256 RGB entries. + //! 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& default_LUT256() { - static CImg palette; - if (!palette) { - palette.assign(1,256,1,3); + static CImg 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) { - palette(0,index,0) = (Tuchar)r; - palette(0,index,1) = (Tuchar)g; - palette(0,index++,2) = (Tuchar)b; + colormap(0,index,0) = (Tuchar)r; + colormap(0,index,1) = (Tuchar)g; + colormap(0,index++,2) = (Tuchar)b; } } - return palette; + cimg::mutex(8,0); + return colormap; } - //! Return palette 'HSV' with 256 RGB entries. + //! 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& HSV_LUT256() { - static CImg palette; - if (!palette) { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { CImg tmp(1,256,1,3,1); tmp.get_shared_channel(0).sequence(0,359); - palette = tmp.HSVtoRGB(); + colormap = tmp.HSVtoRGB(); } - return palette; + cimg::mutex(8,0); + return colormap; } - //! Return palette 'lines' with 256 RGB entries. + //! 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& 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, @@ -18278,154 +27982,349 @@ namespace cimg_library { 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 palette(pal,1,256,1,3,false); - return palette; + static const CImg colormap(pal,1,256,1,3,false); + return colormap; } - //! Return the palette 'hot' with 256 RGB entries. + //! 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& hot_LUT256() { - static CImg palette; - if (!palette) { - palette.assign(1,4,1,3,0); - palette[1] = palette[2] = palette[3] = palette[6] = palette[7] = palette[11] = 255; - palette.resize(1,256,1,3,3); + static CImg 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); } - return palette; + cimg::mutex(8,0); + return colormap; } - //! Return the palette 'cool' with 256 RGB entries. + //! 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& cool_LUT256() { - static CImg palette; - if (!palette) palette.assign(1,2,1,3).fill(0,255,255,0,255,255).resize(1,256,1,3,3); - return palette; + static CImg 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 palette 'jet' with 256 RGB entries. + //! 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& jet_LUT256() { - static CImg palette; - if (!palette) { - palette.assign(1,4,1,3,0); - palette[2] = palette[3] = palette[5] = palette[6] = palette[8] = palette[9] = 255; - palette.resize(1,256,1,3,3); + static CImg 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); } - return palette; + cimg::mutex(8,0); + return colormap; } - //! Return palette 'flag' with 256 RGB entries. + //! 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& flag_LUT256() { - static CImg palette; - if (!palette) { - palette.assign(1,4,1,3,0); - palette[0] = palette[1] = palette[5] = palette[9] = palette[10] = 255; - palette.resize(1,256,1,3,0,2); + static CImg 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); } - return palette; + cimg::mutex(8,0); + return colormap; } - //! Return palette 'cube' with 256 RGB entries. + //! 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& cube_LUT256() { - static CImg palette; - if (!palette) { - palette.assign(1,8,1,3,0); - palette[1] = palette[3] = palette[5] = palette[7] = - palette[10] = palette[11] = palette[12] = palette[13] = - palette[20] = palette[21] = palette[22] = palette[23] = 255; - palette.resize(1,256,1,3,3); + static CImg 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); } - return palette; + cimg::mutex(8,0); + return colormap; } - //! Convert color pixels from sRGB to RGB. + //! Convert pixel values from sRGB to RGB color spaces. CImg& sRGBtoRGB() { - cimg_for(*this,ptr,T) { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32)) + cimg_rof(*this,ptr,T) { const Tfloat - sval = (Tfloat)*ptr, - nsval = (sval<0?0:sval>255?255:sval)/255, - val = (Tfloat)(nsval<=0.04045f?nsval/12.92f:std::pow((nsval+0.055f)/(1.055f),2.4f)); - *ptr = (T)(val*255); + sval = (Tfloat)*ptr/255, + val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f)); + *ptr = (T)cimg::cut(val*255,0,255); } return *this; } + //! Convert pixel values from sRGB to RGB color spaces \newinstance. CImg get_sRGBtoRGB() const { return CImg(*this,false).sRGBtoRGB(); } - //! Convert color pixels from RGB to sRGB. + //! Convert pixel values from RGB to sRGB color spaces. CImg& RGBtosRGB() { - cimg_for(*this,ptr,T) { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32)) + cimg_rof(*this,ptr,T) { const Tfloat - val = (Tfloat)*ptr, - nval = (val<0?0:val>255?255:val)/255, - sval = (Tfloat)(nval<=0.0031308f?nval*12.92f:1.055f*std::pow(nval,0.416667f)-0.055f); - *ptr = (T)(sval*255); + val = (Tfloat)*ptr/255, + sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f); + *ptr = (T)cimg::cut(sval*255,0,255); } return *this; } + //! Convert pixel values from RGB to sRGB color spaces \newinstance. CImg get_RGBtosRGB() const { return CImg(*this,false).RGBtosRGB(); } - //! Convert color pixels from RGB to HSV. - CImg& RGBtoHSV() { + //! Convert pixel values from RGB to HSI color spaces. + CImg& RGBtoHSI() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "RGBtoHSV() : Instance is not a RGB image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256)) + for (ulongT N = 0; N255?255:R))/255, - nG = (G<0?0:(G>255?255:G))/255, - nB = (B<0?0:(B>255?255:B))/255, - m = cimg::min(nR,nG,nB), - M = cimg::max(nR,nG,nB); - Tfloat H = 0, S = 0; - if (M!=m) { - const Tfloat - f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)), - i = (Tfloat)((nR==m)?3:((nG==m)?5:1)); - H = (i-f/(M-m)); - if (H>=6) H-=6; - H*=60; - S = (M-m)/M; - } - *(p1++) = (T)H; - *(p2++) = (T)S; - *(p3++) = (T)M; + 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 get_RGBtoHSI() const { + return CImg(*this,false).RGBtoHSI(); + } + + //! Convert pixel values from HSI to RGB color spaces. + CImg& 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(whd>=256)) + for (ulongT N = 0; N get_HSItoRGB() const { + return CImg< Tuchar>(*this,false).HSItoRGB(); + } + + //! Convert pixel values from RGB to HSL color spaces. + CImg& 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(whd>=256)) + for (ulongT N = 0; N=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 get_RGBtoHSL() const { + return CImg(*this,false).RGBtoHSL(); + } + + //! Convert pixel values from HSL to RGB color spaces. + CImg& 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(whd>=256)) + for (ulongT N = 0; N1?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.0f/3 - ntr):p, + G = 6*ntg<1?p + (q - p)*6*ntg:2*ntg<1?q:3*ntg<2?p + (q - p)*6*(2.0f/3 - ntg):p, + B = 6*ntb<1?p + (q - p)*6*ntb:2*ntb<1?q:3*ntb<2?p + (q - p)*6*(2.0f/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 get_HSLtoRGB() const { + return CImg(*this,false).HSLtoRGB(); + } + + //! Convert pixel values from RGB to HSV color spaces. + CImg& 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(whd>=256)) + for (ulongT N = 0; N=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 get_RGBtoHSV() const { return CImg(*this,false).RGBtoHSV(); } - //! Convert color pixels from HSV to RGB. + //! Convert pixel values from HSV to RGB color spaces. CImg& HSVtoRGB() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "HSVtoRGB() : Instance is not a HSV image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256)) + for (ulongT N = 0; N255?255:R)); - *(p2++) = (T)(G<0?0:(G>255?255:G)); - *(p3++) = (T)(B<0?0:(B>255?255: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 HSV to RGB color spaces \newinstance. CImg get_HSVtoRGB() const { return CImg(*this,false).HSVtoRGB(); } - //! Convert color pixels from RGB to HSL. - CImg& 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); - for (unsigned int N = _width*_height*_depth; N; --N) { - const Tfloat - R = (Tfloat)*p1, - G = (Tfloat)*p2, - B = (Tfloat)*p3, - nR = (R<0?0:(R>255?255:R))/255, - nG = (G<0?0:(G>255?255:G))/255, - nB = (B<0?0:(B>255?255:B))/255, - m = cimg::min(nR,nG,nB), - M = cimg::max(nR,nG,nB), - L = (m + M)/2; - Tfloat H = 0, S = 0; - if (M==m) H = S = 0; - else { - const Tfloat - f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)), - i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f); - H = (i-f/(M-m)); - if (H>=6) H-=6; - H*=60; - S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m)); - } - *(p1++) = (T)H; - *(p2++) = (T)S; - *(p3++) = (T)L; - } - return *this; - } - - CImg get_RGBtoHSL() const { - return CImg< Tfloat>(*this,false).RGBtoHSL(); - } - - //! Convert color pixels from HSL to RGB. - CImg& 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); - for (unsigned int N = _width*_height*_depth; N; --N) { - const Tfloat - H = (Tfloat)*p1, - S = (Tfloat)*p2, - L = (Tfloat)*p3, - q = 2*L<1?L*(1+S):(L+S-L*S), - p = 2*L-q, - h = H/360, - tr = h + 1.0f/3, - tg = h, - tb = h - 1.0f/3, - ntr = tr<0?tr+1:(tr>1?tr-1:tr), - ntg = tg<0?tg+1:(tg>1?tg-1:tg), - ntb = tb<0?tb+1:(tb>1?tb-1:tb), - R = 255*(6*ntr<1?p+(q-p)*6*ntr:(2*ntr<1?q:(3*ntr<2?p+(q-p)*6*(2.0f/3-ntr):p))), - G = 255*(6*ntg<1?p+(q-p)*6*ntg:(2*ntg<1?q:(3*ntg<2?p+(q-p)*6*(2.0f/3-ntg):p))), - B = 255*(6*ntb<1?p+(q-p)*6*ntb:(2*ntb<1?q:(3*ntb<2?p+(q-p)*6*(2.0f/3-ntb):p))); - *(p1++) = (T)(R<0?0:(R>255?255:R)); - *(p2++) = (T)(G<0?0:(G>255?255:G)); - *(p3++) = (T)(B<0?0:(B>255?255:B)); - } - return *this; - } - - CImg get_HSLtoRGB() const { - return CImg(*this,false).HSLtoRGB(); - } - - //! Convert color pixels from RGB to HSI. - //! Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002. - CImg& 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); - for (unsigned int N = _width*_height*_depth; N; --N) { - const Tfloat - R = (Tfloat)*p1, - G = (Tfloat)*p2, - B = (Tfloat)*p3, - nR = (R<0?0:(R>255?255:R))/255, - nG = (G<0?0:(G>255?255:G))/255, - nB = (B<0?0:(B>255?255:B))/255, - m = cimg::min(nR,nG,nB), - theta = (Tfloat)(std::acos(0.5f*((nR-nG)+(nR-nB))/std::sqrt(std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::PI), - sum = nR + nG + nB; - Tfloat H = 0, S = 0, I = 0; - if (theta>0) H = (nB<=nG)?theta:360-theta; - if (sum>0) S = 1 - 3/sum*m; - I = sum/3; - *(p1++) = (T)H; - *(p2++) = (T)S; - *(p3++) = (T)I; - } - return *this; - } - - CImg get_RGBtoHSI() const { - return CImg(*this,false).RGBtoHSI(); - } - - //! Convert color pixels from HSI to RGB. - CImg& 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); - for (unsigned int N = _width*_height*_depth; N; --N) { - Tfloat - H = (Tfloat)*p1, - S = (Tfloat)*p2, - I = (Tfloat)*p3, - 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); - } - R*=255; G*=255; B*=255; - *(p1++) = (T)(R<0?0:(R>255?255:R)); - *(p2++) = (T)(G<0?0:(G>255?255:G)); - *(p3++) = (T)(B<0?0:(B>255?255:B)); - } - return *this; - } - - CImg get_HSItoRGB() const { - return CImg< Tuchar>(*this,false).HSItoRGB(); - } - - //! Convert color pixels from RGB to YCbCr. + //! Convert pixel values from RGB to YCbCr color spaces. CImg& RGBtoYCbCr() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "RGBtoYCbCr() : Instance is not a RGB image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512)) + for (ulongT N = 0; N255?255:Y)); - *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb)); - *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr)); + 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 get_RGBtoYCbCr() const { return CImg(*this,false).RGBtoYCbCr(); } - //! Convert color pixels from RGB to YCbCr. + //! Convert pixel values from RGB to YCbCr color spaces. CImg& YCbCrtoRGB() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "YCbCrtoRGB() : Instance is not a YCbCr image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512)) + for (ulongT N = 0; N255?255:R)); - *(p2++) = (T)(G<0?0:(G>255?255:G)); - *(p3++) = (T)(B<0?0:(B>255?255:B)); + 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 get_YCbCrtoRGB() const { return CImg(*this,false).YCbCrtoRGB(); } - //! Convert color pixels from RGB to YUV. + //! Convert pixel values from RGB to YUV color spaces. CImg& RGBtoYUV() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "RGBtoYUV() : Instance is not a RGB image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384)) + for (ulongT N = 0; N get_RGBtoYUV() const { return CImg(*this,false).RGBtoYUV(); } - //! Convert color pixels from YUV to RGB. + //! Convert pixel values from YUV to RGB color spaces. CImg& YUVtoRGB() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "YUVtoRGB() : Instance is not a YUV image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384)) + for (ulongT N = 0; N255?255:R)); - *(p2++) = (T)(G<0?0:(G>255?255:G)); - *(p3++) = (T)(B<0?0:(B>255?255:B)); + 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 get_YUVtoRGB() const { return CImg< Tuchar>(*this,false).YUVtoRGB(); } - //! Convert color pixels from RGB to CMY. + //! Convert pixel values from RGB to CMY color spaces. CImg& RGBtoCMY() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "RGBtoCMY() : Instance is not a RGB image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048)) + for (ulongT N = 0; N255?255:C)); - *(p2++) = (T)(M<0?0:(M>255?255:M)); - *(p3++) = (T)(Y<0?0:(Y>255?255:Y)); + 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 get_RGBtoCMY() const { return CImg(*this,false).RGBtoCMY(); } - //! Convert CMY pixels of a color image into the RGB color space. + //! Convert pixel values from CMY to RGB color spaces. CImg& CMYtoRGB() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "CMYtoRGB() : Instance is not a CMY image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048)) + for (ulongT N = 0; N255?255:R)); - *(p2++) = (T)(G<0?0:(G>255?255:G)); - *(p3++) = (T)(B<0?0:(B>255?255:B)); + 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 get_CMYtoRGB() const { return CImg(*this,false).CMYtoRGB(); } - //! Convert color pixels from CMY to CMYK. + //! Convert pixel values from CMY to CMYK color spaces. CImg& CMYtoCMYK() { return get_CMYtoCMYK().move_to(*this); } + //! Convert pixel values from CMY to CMYK color spaces \newinstance. CImg get_CMYtoCMYK() const { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "CMYtoCMYK() : Instance is not a CMY image.", + "CMYtoCMYK(): Instance is not a CMY image.", cimg_instance); CImg 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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024)) + for (ulongT N = 0; N=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++) = (Tfloat)(C<0?0:(C>255?255:C)); - *(pd2++) = (Tfloat)(M<0?0:(M>255?255:M)); - *(pd3++) = (Tfloat)(Y<0?0:(Y>255?255:Y)); - *(pd4++) = (Tfloat)(K<0?0:(K>255?255:K)); + 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 CMYK pixels of a color image into the CMY color space. + //! Convert pixel values from CMYK to CMY color spaces. CImg& CMYKtoCMY() { return get_CMYKtoCMY().move_to(*this); } + //! Convert pixel values from CMYK to CMY color spaces \newinstance. CImg get_CMYKtoCMY() const { if (_spectrum!=4) throw CImgInstanceException(_cimg_instance - "CMYKtoCMY() : Instance is not a CMYK image.", + "CMYKtoCMY(): Instance is not a CMYK image.", cimg_instance); CImg 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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024)) + for (ulongT N = 0; N255?255:nC)); - *(pd2++) = (Tfloat)(nM<0?0:(nM>255?255:nM)); - *(pd3++) = (Tfloat)(nY<0?0:(nY>255?255:nY)); + 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 color pixels from RGB to XYZ_709. - CImg& RGBtoXYZ() { + //! Convert pixel values from RGB to XYZ color spaces. + /** + \param use_D65 Tell to use the D65 illuminant (D50 otherwise). + **/ + CImg& RGBtoXYZ(const bool use_D65=true) { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "RGBtoXYZ() : Instance is not a RGB image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048)) + for (ulongT N = 0; N get_RGBtoXYZ() const { - return CImg(*this,false).RGBtoXYZ(); + //! Convert pixel values from RGB to XYZ color spaces \newinstance. + CImg get_RGBtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).RGBtoXYZ(use_D65); } - //! Convert XYZ_709 pixels of a color image into the RGB color space. - CImg& XYZtoRGB() { + //! Convert pixel values from XYZ to RGB color spaces. + /** + \param use_D65 Tell to use the D65 illuminant (D50 otherwise). + **/ + CImg& XYZtoRGB(const bool use_D65=true) { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "XYZtoRGB() : Instance is not a XYZ image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048)) + for (ulongT N = 0; N255?255:R)); - *(p2++) = (T)(G<0?0:(G>255?255:G)); - *(p3++) = (T)(B<0?0:(B>255?255:B)); + 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; } - CImg get_XYZtoRGB() const { - return CImg(*this,false).XYZtoRGB(); + //! Convert pixel values from XYZ to RGB color spaces \newinstance. + CImg get_XYZtoRGB(const bool use_D65=true) const { + return CImg(*this,false).XYZtoRGB(use_D65); } - //! Convert XYZ_709 pixels of a color image into the (L*,a*,b*) color space. - CImg& XYZtoLab() { -#define _cimg_Labf(x) ((x)>=0.008856f?(std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116)) + //! Convert pixel values from XYZ to Lab color spaces. + CImg& 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.", + "XYZtoLab(): Instance is not a XYZ image.", cimg_instance); - - const Tfloat - Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f), - Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f), - Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f); + const CImg white = CImg(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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128)) + for (ulongT N = 0; N get_XYZtoLab() const { - return CImg(*this,false).XYZtoLab(); + //! Convert pixel values from XYZ to Lab color spaces \newinstance. + CImg get_XYZtoLab(const bool use_D65=true) const { + return CImg(*this,false).XYZtoLab(use_D65); } - //! Convert Lab pixels of a color image into the XYZ color space. - CImg& LabtoXYZ() { -#define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f)) - + //! Convert pixel values from Lab to XYZ color spaces. + CImg& LabtoXYZ(const bool use_D65=true) { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "LabtoXYZ() : Instance is not a Lab image.", + "LabtoXYZ(): Instance is not a Lab image.", cimg_instance); - - const Tfloat - Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f), - Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f), - Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f); + const CImg white = CImg(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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128)) + for (ulongT N = 0; N216?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; } - CImg get_LabtoXYZ() const { - return CImg(*this,false).LabtoXYZ(); + //! Convert pixel values from Lab to XYZ color spaces \newinstance. + CImg get_LabtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).LabtoXYZ(use_D65); } - //! Convert XYZ_709 pixels of a color image into the xyY color space. + //! Convert pixel values from XYZ to xyY color spaces. CImg& XYZtoxyY() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "XYZtoxyY() : Instance is not a XYZ image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096)) + for (ulongT N = 0; N0?sum:1; - *(p1++) = (T)(X/nsum); - *(p2++) = (T)(Y/nsum); - *(p3++) = (T)Y; + 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 get_XYZtoxyY() const { return CImg(*this,false).XYZtoxyY(); } - //! Convert xyY pixels of a color image into the XYZ_709 color space. + //! Convert pixel values from xyY pixels to XYZ color spaces. CImg& xyYtoXYZ() { if (_spectrum!=3) throw CImgInstanceException(_cimg_instance - "xyYtoXYZ() : Instance is not a xyY image.", + "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); - for (unsigned int N = _width*_height*_depth; N; --N) { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096)) + for (ulongT N = 0; N0?py:1; - *(p1++) = (T)(px*Y/ny); - *(p2++) = (T)Y; - *(p3++) = (T)((1-px-py)*Y/ny); + 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 get_xyYtoXYZ() const { return CImg(*this,false).xyYtoXYZ(); } - //! Convert a RGB image to a Lab one. - CImg& RGBtoLab() { - return RGBtoXYZ().XYZtoLab(); + //! Convert pixel values from RGB to Lab color spaces. + CImg& RGBtoLab(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoLab(use_D65); } - CImg get_RGBtoLab() const { - return CImg(*this,false).RGBtoLab(); + //! Convert pixel values from RGB to Lab color spaces \newinstance. + CImg get_RGBtoLab(const bool use_D65=true) const { + return CImg(*this,false).RGBtoLab(use_D65); } - //! Convert a Lab image to a RGB one. - CImg& LabtoRGB() { - return LabtoXYZ().XYZtoRGB(); + //! Convert pixel values from Lab to RGB color spaces. + CImg& LabtoRGB(const bool use_D65=true) { + return LabtoXYZ().XYZtoRGB(use_D65); } - CImg get_LabtoRGB() const { - return CImg(*this,false).LabtoRGB(); + //! Convert pixel values from Lab to RGB color spaces \newinstance. + CImg get_LabtoRGB(const bool use_D65=true) const { + return CImg(*this,false).LabtoRGB(use_D65); } - //! Convert a RGB image to a xyY one. - CImg& RGBtoxyY() { - return RGBtoXYZ().XYZtoxyY(); + //! Convert pixel values from RGB to xyY color spaces. + CImg& RGBtoxyY(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoxyY(); } - CImg get_RGBtoxyY() const { - return CImg(*this,false).RGBtoxyY(); + //! Convert pixel values from RGB to xyY color spaces \newinstance. + CImg get_RGBtoxyY(const bool use_D65=true) const { + return CImg(*this,false).RGBtoxyY(use_D65); } - //! Convert a xyY image to a RGB one. - CImg& xyYtoRGB() { - return xyYtoXYZ().XYZtoRGB(); + //! Convert pixel values from xyY to RGB color spaces. + CImg& xyYtoRGB(const bool use_D65=true) { + return xyYtoXYZ().XYZtoRGB(use_D65); } - CImg get_xyYtoRGB() const { - return CImg(*this,false).xyYtoRGB(); + //! Convert pixel values from xyY to RGB color spaces \newinstance. + CImg get_xyYtoRGB(const bool use_D65=true) const { + return CImg(*this,false).xyYtoRGB(use_D65); } - //! Convert a RGB image to a CMYK one. + //! Convert pixel values from RGB to CMYK color spaces. CImg& RGBtoCMYK() { return RGBtoCMY().CMYtoCMYK(); } + //! Convert pixel values from RGB to CMYK color spaces \newinstance. CImg get_RGBtoCMYK() const { return CImg(*this,false).RGBtoCMYK(); } - //! Convert a CMYK image to a RGB one. + //! Convert pixel values from CMYK to RGB color spaces. CImg& CMYKtoRGB() { return CMYKtoCMY().CMYtoRGB(); } + //! Convert pixel values from CMYK to RGB color spaces \newinstance. CImg get_CMYKtoRGB() const { return CImg(*this,false).CMYKtoRGB(); } - //! Convert a RGB image to a Bayer-coded representation. - /** - \note First (upper-left) pixel if the red component of the pixel color. - **/ - CImg& RGBtoBayer() { - return get_RGBtoBayer().move_to(*this); - } - - CImg get_RGBtoBayer() const { - if (_spectrum!=3) - throw CImgInstanceException(_cimg_instance - "RGBtoBayer() : Instance is not a RGB image.", - cimg_instance); - - CImg res(_width,_height,_depth,1); - 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; - cimg_forXYZ(*this,x,y,z) { - if (y%2) { - if (x%2) *(ptrd++) = *ptr_b; - else *(ptrd++) = *ptr_g; - } else { - if (x%2) *(ptrd++) = *ptr_g; - else *(ptrd++) = *ptr_r; - } - ++ptr_r; ++ptr_g; ++ptr_b; - } - return res; - } - - //! Convert a Bayer-coded image to a RGB color image. - CImg& BayertoRGB(const unsigned int interpolation_type=3) { - return get_BayertoRGB(interpolation_type).move_to(*this); - } - - CImg get_BayertoRGB(const unsigned int interpolation_type=3) const { - if (_spectrum!=1) - throw CImgInstanceException(_cimg_instance - "BayertoRGB() : Instance is not a Bayer image.", - cimg_instance); - - CImg res(_width,_height,_depth,3); - CImg_3x3(I,T); - Tuchar *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); - switch (interpolation_type) { - case 3 : { // Edge-directed - CImg_3x3(R,T); - CImg_3x3(G,T); - CImg_3x3(B,T); - cimg_forXYZ(*this,x,y,z) { - const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x& 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 border_conditions=0, + 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 = (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,0); + 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,border_conditions,centering_x,centering_y,centering_z,centering_c).move_to(*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 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 border_conditions=0, + 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].", + "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(); 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; + 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(sx,sy,sz,sc,0); - + if (is_empty()) return CImg(sx,sy,sz,sc,(T)0); CImg res; switch (interpolation_type) { // Raw resizing. // case -1 : - std::memcpy(res.assign(sx,sy,sz,sc,0)._data,_data,sizeof(T)*cimg::min(size(),sx*sy*sz*sc)); + 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. @@ -19290,58 +28944,76 @@ namespace cimg_library { zc = (int)(centering_z*((int)sz - depth())), cc = (int)(centering_c*((int)sc - spectrum())); - switch (border_conditions) { - case 2 : { // Cyclic borders. + 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(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=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 borders. + case 1 : { // Neumann res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this); CImg 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); + 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 (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); + 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 (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); + 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 (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); + 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); + 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 borders. - res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this); + default : // Dirichlet + res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this); } break; } break; @@ -19350,61 +29022,79 @@ namespace cimg_library { // case 1 : { res.assign(sx,sy,sz,sc); - CImg off_x(sx), off_y(sy+1), off_z(sz+1), off_c(sc+1); - unsigned long *poff_x, *poff_y, *poff_z, *poff_c, curr, old; - const unsigned long - wh = (unsigned long)_width*_height, - whd = (unsigned long)_width*_height*_depth, - sxy = (unsigned long)sx*sy, - sxyz = (unsigned long)sx*sy*sz; + CImg 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; if (sx==_width) off_x.fill(1); else { - poff_x = off_x._data; curr = 0; - cimg_forX(res,x) { old = curr; curr = ((x+1LU)*_width/sx); *(poff_x++) = curr - old; } + ulongT *poff_x = off_x._data, curr = 0; + cimg_forX(res,x) { + const ulongT old = curr; + curr = (ulongT)((x + 1.0)*_width/sx); + *(poff_x++) = curr - old; + } } if (sy==_height) off_y.fill(_width); else { - poff_y = off_y._data; curr = 0; - cimg_forY(res,y) { old = curr; curr = ((y+1LU)*_height/sy); *(poff_y++) = _width*(curr - old); } *poff_y = 0; + ulongT *poff_y = off_y._data, curr = 0; + cimg_forY(res,y) { + const ulongT old = curr; + curr = (ulongT)((y + 1.0)*_height/sy); + *(poff_y++) = _width*(curr - old); + } + *poff_y = 0; } if (sz==_depth) off_z.fill(wh); else { - poff_z = off_z._data; curr = 0; - cimg_forZ(res,z) { old = curr; curr = ((z+1LU)*_depth/sz); *(poff_z++) = wh*(curr - old); } *poff_z = 0; + ulongT *poff_z = off_z._data, curr = 0; + cimg_forZ(res,z) { + const ulongT old = curr; + curr = (ulongT)((z + 1.0)*_depth/sz); + *(poff_z++) = wh*(curr - old); + } + *poff_z = 0; } if (sc==_spectrum) off_c.fill(whd); else { - poff_c = off_c._data; curr = 0; - cimg_forC(res,c) { old = curr; curr = ((c+1LU)*_spectrum/sc); *(poff_c++) = whd*(curr - old); } *poff_c = 0; + ulongT *poff_c = off_c._data, curr = 0; + cimg_forC(res,c) { + const ulongT old = curr; + curr = (ulongT)((c + 1.0)*_spectrum/sc); + *(poff_c++) = whd*(curr - old); + } + *poff_c = 0; } T *ptrd = res._data; - const T* ptrv = _data; - poff_c = off_c._data; + const T* ptrc = _data; + const ulongT *poff_c = off_c._data; for (unsigned int c = 0; c 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 = cimg::min(b,c); + 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 (!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); @@ -19427,11 +29121,17 @@ namespace cimg_library { if (sy!=_height) { CImg 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 = cimg::min(b,c); + 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 (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); @@ -19440,11 +29140,17 @@ namespace cimg_library { if (sz!=_depth) { CImg 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 = cimg::min(b,c); + 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 (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); @@ -19453,11 +29159,17 @@ namespace cimg_library { if (sc!=_spectrum) { CImg 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 = cimg::min(b,c); + 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 (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); @@ -19469,31 +29181,38 @@ namespace cimg_library { // case 3 : { CImg off(cimg::max(sx,sy,sz,sc)); - CImg foff(off._width); - unsigned int *poff; - float *pfoff, old, curr; + CImg foff(off._width); CImg resx, resy, resz, resc; - T *ptrd; + 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 float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx; + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0): + (double)_width/sx; resx.assign(sx,_height,_depth,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; } - ptrd = resx._data; - const T *ptrs0 = _data; - cimg_forYZC(resx,y,z,c) { - poff = off._data; pfoff = foff._data; - const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-1); + 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.0,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 float alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy; - resy.assign(sx,sy,_depth,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); } - cimg_forXZC(resy,x,z,c) { - ptrd = resy.data(x,0,z,c); - const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height-1)*sx; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs=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_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz; - const unsigned int sxy = sx*sy; - resz.assign(sx,sy,sz,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } - cimg_forXYC(resz,x,y,c) { - ptrd = resz.data(x,y,0,c); - const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth-1)*sxy; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs=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_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc; - const unsigned int sxyz = sx*sy*sz; - resc.assign(sx,sy,sz,sc); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } - cimg_forXYZ(resc,x,y,z) { - ptrd = resc.data(x,y,z,0); - const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum-1)*sxyz; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float alpha = *(pfoff++); - const T val1 = *ptrs, val2 = ptrs=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 resx, resy, resz, resc; - const unsigned int sxy = sx*sy, sxyz = sx*sy*sz; if (sx!=_width) { if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); else { - resx.assign(sx,_height,_depth,_spectrum,0); - const T *ptrs = _data; - T *ptrd = resx._data + (int)(centering_x*(sx-1)/_width); - cimg_forYZC(*this,y,z,c) { - cimg_forX(*this,x) ptrd[x*sx/_width] = *(ptrs++); - ptrd+=sx; + 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); @@ -19592,12 +29350,13 @@ namespace cimg_library { if (sy!=_height) { if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); else { - resy.assign(sx,sy,_depth,_spectrum,0); - const T *ptrs = resx._data; - T *ptrd = resy._data + (int)(centering_y*(sy-1)/_height)*sx; - cimg_forZC(*this,z,c) { - cimg_forY(*this,y) { std::memcpy(ptrd + (y*sy/_height)*sx,ptrs,sizeof(T)*sx); ptrs+=sx; } - ptrd+=sxy; + 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(); @@ -19606,12 +29365,13 @@ namespace cimg_library { if (sz!=_depth) { if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); else { - resz.assign(sx,sy,sz,_spectrum,0); - const T *ptrs = resy._data; - T *ptrd = resz._data + (int)(centering_z*(sz-1)/_depth)*sxy; - cimg_forC(*this,c) { - cimg_forZ(*this,z) { std::memcpy(ptrd + (z*sz/_depth)*sxy,ptrs,sizeof(T)*sxy); ptrs+=sxy; } - ptrd+=sxyz; + 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(); @@ -19620,10 +29380,14 @@ namespace cimg_library { if (sc!=_spectrum) { if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); else { - resc.assign(sx,sy,sz,sc,0); - const T *ptrs = resz._data; - T *ptrd = resc._data + (int)(centering_c*(sc-1)/_spectrum)*sxyz; - cimg_forC(*this,c) { std::memcpy(ptrd + (c*sc/_spectrum)*sxyz,ptrs,sizeof(T)*sxyz); ptrs+=sxyz; } + 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); @@ -19631,41 +29395,51 @@ namespace cimg_library { return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; } break; - // Bicubic interpolation. + // Cubic interpolation. // case 5 : { const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); CImg off(cimg::max(sx,sy,sz,sc)); - CImg foff(off._width); - unsigned int *poff; - float *pfoff, old, curr; + CImg foff(off._width); CImg resx, resy, resz, resc; - T *ptrd; + double curr, old; if (sx!=_width) { if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); else { - const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx; - resx.assign(sx,_height,_depth,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; } - ptrd = resx._data; - const T *ptrs0 = _data; - cimg_forYZC(resx,y,z,c) { - poff = off._data; pfoff = foff._data; - const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-2); + 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.0)/(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) { - const float t = *(pfoff++); - const Tfloat - val1 = (Tfloat)*ptrs, - val0 = ptrs>ptrs0?(Tfloat)*(ptrs-1):val1, - val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val1, - val3 = ptrsvmax?vmax:val); - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.0,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } } - ptrs0+=_width; } } } else resx.assign(*this,true); @@ -19673,25 +29447,39 @@ namespace cimg_library { if (sy!=_height) { if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); else { - const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy; - resy.assign(sx,sy,_depth,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); } - cimg_forXZC(resy,x,z,c) { - ptrd = resy.data(x,0,z,c); - const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_height-2)*sx; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float t = *(pfoff++); - const Tfloat - val1 = (Tfloat)*ptrs, - val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sx):val1, - val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val1, - val3 = ptrsvmax?vmax:val); - ptrd+=sx; - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.0,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } } } } @@ -19701,26 +29489,40 @@ namespace cimg_library { if (sz!=_depth) { if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); else { - const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz; - const unsigned int sxy = sx*sy; - resz.assign(sx,sy,sz,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } - cimg_forXYC(resz,x,y,c) { - ptrd = resz.data(x,y,0,c); - const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_depth-2)*sxy; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float t = *(pfoff++); - const Tfloat - val1 = (Tfloat)*ptrs, - val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxy):val1, - val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val1, - val3 = ptrsvmax?vmax:val); - ptrd+=sxy; - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.0,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } } } } @@ -19730,26 +29532,40 @@ namespace cimg_library { if (sc!=_spectrum) { if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); else { - const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc; - const unsigned int sxyz = sx*sy*sz; - resc.assign(sx,sy,sz,sc); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } - cimg_forXYZ(resc,x,y,z) { - ptrd = resc.data(x,y,z,0); - const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum-2)*sxyz; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float t = *(pfoff++); - const Tfloat - val1 = (Tfloat)*ptrs, - val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxyz):val1, - val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val1, - val3 = ptrsvmax?vmax:val); - ptrd+=sxyz; - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.0,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } } } } @@ -19762,45 +29578,54 @@ namespace cimg_library { // Lanczos interpolation. // case 6 : { - const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + const double vmin = (double)cimg::type::min(), vmax = (double)cimg::type::max(); CImg off(cimg::max(sx,sy,sz,sc)); - CImg foff(off._width); - unsigned int *poff; - float *pfoff, old, curr; + CImg foff(off._width); CImg resx, resy, resz, resc; - T *ptrd; + double curr, old; if (sx!=_width) { if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); else { - const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx; - resx.assign(sx,_height,_depth,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; } - ptrd = resx._data; - const T *ptrs0 = _data; - cimg_forYZC(resx,y,z,c) { - poff = off._data; pfoff = foff._data; - const T *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, *const ptrsmax = ptrs0 + (_width-2); + 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.0)/(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) { - const float - 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); - const Tfloat - val2 = (Tfloat)*ptrs, - val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-1):val2, - val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2):val1, - val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val2, - val4 = ptrsvmax?vmax:val); - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.0,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } } - ptrs0+=_width; } } } else resx.assign(*this,true); @@ -19808,32 +29633,45 @@ namespace cimg_library { if (sy!=_height) { if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); else { - const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy; - resy.assign(sx,sy,_depth,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); } - cimg_forXZC(resy,x,z,c) { - ptrd = resy.data(x,0,z,c); - const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, *const ptrsmax = ptrs0 + (_height-2)*sx; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float - 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); - const Tfloat - val2 = (Tfloat)*ptrs, - val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sx):val2, - val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sx):val1, - val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val2, - val4 = ptrsvmax?vmax:val); - ptrd+=sx; - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.0,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } } } } @@ -19843,33 +29681,46 @@ namespace cimg_library { if (sz!=_depth) { if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); else { - const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz; - const unsigned int sxy = sx*sy; - resz.assign(sx,sy,sz,_spectrum); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } - cimg_forXYC(resz,x,y,c) { - ptrd = resz.data(x,y,0,c); - const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, *const ptrsmax = ptrs0 + (_depth-2)*sxy; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float - 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); - const Tfloat - val2 = (Tfloat)*ptrs, - val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxy):val2, - val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxy):val1, - val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val2, - val4 = ptrsvmax?vmax:val); - ptrd+=sxy; - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.0,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } } } } @@ -19879,33 +29730,46 @@ namespace cimg_library { if (sc!=_spectrum) { if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); else { - const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc; - const unsigned int sxyz = sx*sy*sz; - resc.assign(sx,sy,sz,sc); - curr = old = 0; poff = off._data; pfoff = foff._data; - cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } - cimg_forXYZ(resc,x,y,z) { - ptrd = resc.data(x,y,z,0); - const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, *const ptrsmax = ptrs + (_spectrum-2)*sxyz; - poff = off._data; pfoff = foff._data; + 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.0)/(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) { - const float - 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); - const Tfloat - val2 = (Tfloat)*ptrs, - val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxyz):val2, - val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxyz):val1, - val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val2, - val4 = ptrsvmax?vmax:val); - ptrd+=sxyz; - ptrs+=*(poff++); + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.0,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } } } } @@ -19919,84 +29783,106 @@ namespace cimg_library { // 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=bicubic | 6=lanczos }).", + "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 an image. + //! 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 CImg& resize(const CImg& src, - const int interpolation_type=1, const unsigned int border_conditions=0, + 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,border_conditions, + 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 CImg get_resize(const CImg& src, - const int interpolation_type=1, const unsigned int border_conditions=0, + 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,border_conditions, + return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, centering_x,centering_y,centering_z,centering_c); } - //! Resize an image. + //! 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& resize(const CImgDisplay& disp, - const int interpolation_type=1, const unsigned int border_conditions=0, + 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,border_conditions, + 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 get_resize(const CImgDisplay& disp, - const int interpolation_type=1, const unsigned int border_conditions=0, + 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,border_conditions, + return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, centering_x,centering_y,centering_z,centering_c); } - //! Half-resize an image, using a special optimized filter. + //! Resize image to half-size along XY axes, using an optimized filter. CImg& resize_halfXY() { return get_resize_halfXY().move_to(*this); } + //! Resize image to half-size along XY axes, using an optimized filter \newinstance. CImg get_resize_halfXY() const { if (is_empty()) return *this; - const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, - 0.1231940459f, 0.1935127547f, 0.1231940459f, - 0.07842776544f, 0.1231940459f, 0.07842776544f }; - T I[9] = { 0 }; - CImg res(_width/2,_height/2,_depth,_spectrum); + static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f }; + CImg 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]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + - I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] + - I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]); + (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; } - //! Upscale an image by a factor 2x. + //! Resize image to double-size, using the Scale2X algorithm. /** - Use anisotropic upscaling algorithm described at - http://scale2x.sourceforge.net/algorithm.html + \note Use anisotropic upscaling algorithm + described here. **/ CImg& resize_doubleXY() { return get_resize_doubleXY().move_to(*this); } + //! Resize image to double-size, using the Scale2X algorithm \newinstance. CImg 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 = 1>=(bound)?(int)(bound) - 1:1; \ _n1##i<(int)(bound) || i==--_n1##i; \ _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width) @@ -20007,7 +29893,7 @@ namespace cimg_library { (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); \ + 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)), \ @@ -20037,19 +29923,20 @@ namespace cimg_library { return res; } - //! Upscale an image by a factor 3x. + //! Resize image to triple-size, using the Scale3X algorithm. /** - Use anisotropic upscaling algorithm described at - http://scale2x.sourceforge.net/algorithm.html + \note Use anisotropic upscaling algorithm + described here. **/ CImg& resize_tripleXY() { return get_resize_tripleXY().move_to(*this); } + //! Resize image to triple-size, using the Scale3X algorithm \newinstance. CImg 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 = 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) @@ -20060,7 +29947,7 @@ namespace cimg_library { (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); \ + 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)), \ @@ -20100,13 +29987,16 @@ namespace cimg_library { return res; } - //! Mirror an image along the specified axis. + //! Mirror image content along specified axis. + /** + \param axis Mirror axis + **/ CImg& mirror(const char axis) { if (is_empty()) return *this; T *pf, *pb, *buf = 0; - switch (cimg::uncase(axis)) { + switch (cimg::lowercase(axis)) { case 'x' : { - pf = _data; pb = data(_width-1); + 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 get_mirror(const char axis) const { return (+*this).mirror(axis); } - //! Shift the image. + //! Mirror image content along specified axes. /** - \param deltax Amount of displacement along the X-axis. - \param deltay Amount of displacement along the Y-axis. - \param deltaz Amount of displacement along the Z-axis. - \param deltac Amount of displacement along the C-axis. - \param border_condition Border condition. - - - \c border_condition can be : - - 0 : Zero border condition (Dirichlet). - - 1 : Nearest neighbors (Neumann). - - 2 : Repeat Pattern (Fourier style). + \param axes Mirror axes, as a C-string. + \note \c axes may contains multiple characters, e.g. \c "xyz" **/ - CImg& shift(const int deltax, const int deltay=0, const int deltaz=0, const int deltac=0, - const int border_condition=0) { + CImg& mirror(const char *const axes) { + for (const char *s = axes; *s; ++s) mirror(*s); + return *this; + } + + //! Mirror image content along specified axes \newinstance. + CImg 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& 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 (deltax) // Shift along X-axis - switch (border_condition) { - case 0 : - if (cimg::abs(deltax)>=width()) return fill(0); - if (deltax<0) cimg_forYZC(*this,y,z,c) { - std::memmove(data(0,y,z,c),data(-deltax,y,z,c),(_width+deltax)*sizeof(T)); - std::memset(data(_width+deltax,y,z,c),0,-deltax*sizeof(T)); - } else cimg_forYZC(*this,y,z,c) { - std::memmove(data(deltax,y,z,c),data(0,y,z,c),(_width-deltax)*sizeof(T)); - std::memset(data(0,y,z,c),0,deltax*sizeof(T)); - } - break; - case 1 : - if (deltax<0) { - const int ndeltax = (-deltax>=width())?_width-1:-deltax; - if (!ndeltax) 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 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(ndeltax,y,z,c),(_width-ndeltax)*sizeof(T)); - T *ptrd = data(_width-1,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=width())?_width-1:deltax; - if (!ndeltax) return *this; + 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(ndeltax,y,z,c),data(0,y,z,c),(_width-ndeltax)*sizeof(T)); + 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; l0) cimg_forYZC(*this,y,z,c) { - std::memcpy(buf,data(0,y,z,c),ndeltax*sizeof(T)); - std::memmove(data(0,y,z,c),data(ndeltax,y,z,c),(_width-ndeltax)*sizeof(T)); - std::memcpy(data(_width-ndeltax,y,z,c),buf,ndeltax*sizeof(T)); - } else cimg_forYZC(*this,y,z,c) { - std::memcpy(buf,data(_width+ndeltax,y,z,c),-ndeltax*sizeof(T)); - std::memmove(data(-ndeltax,y,z,c),data(0,y,z,c),(_width+ndeltax)*sizeof(T)); - std::memcpy(data(0,y,z,c),buf,-ndeltax*sizeof(T)); - } - delete[] buf; - } + default : // Dirichlet + if (cimg::abs(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 (deltay) // Shift along Y-axis - switch (border_condition) { - case 0 : - if (cimg::abs(deltay)>=height()) return fill(0); - if (deltay<0) cimg_forZC(*this,z,c) { - std::memmove(data(0,0,z,c),data(0,-deltay,z,c),_width*(_height+deltay)*sizeof(T)); - std::memset(data(0,_height+deltay,z,c),0,-deltay*_width*sizeof(T)); - } else cimg_forZC(*this,z,c) { - std::memmove(data(0,deltay,z,c),data(0,0,z,c),_width*(_height-deltay)*sizeof(T)); - std::memset(data(0,0,z,c),0,deltay*_width*sizeof(T)); - } - break; - case 1 : - if (deltay<0) { - const int ndeltay = (-deltay>=height())?_height-1:-deltay; - if (!ndeltay) return *this; + 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 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,ndeltay,z,c),_width*(_height-ndeltay)*sizeof(T)); - T *ptrd = data(0,_height-ndeltay,z,c), *ptrs = data(0,_height-1,z,c); - for (int l = 0; l=height())?_height-1:deltay; - if (!ndeltay) return *this; + 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,ndeltay,z,c),data(0,0,z,c),_width*(_height-ndeltay)*sizeof(T)); + 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; l0) cimg_forZC(*this,z,c) { - std::memcpy(buf,data(0,0,z,c),_width*ndeltay*sizeof(T)); - std::memmove(data(0,0,z,c),data(0,ndeltay,z,c),_width*(_height-ndeltay)*sizeof(T)); - std::memcpy(data(0,_height-ndeltay,z,c),buf,_width*ndeltay*sizeof(T)); - } else cimg_forZC(*this,z,c) { - std::memcpy(buf,data(0,_height+ndeltay,z,c),-ndeltay*_width*sizeof(T)); - std::memmove(data(0,-ndeltay,z,c),data(0,0,z,c),_width*(_height+ndeltay)*sizeof(T)); - std::memcpy(data(0,0,z,c),buf,-ndeltay*_width*sizeof(T)); - } - delete[] buf; - } + default : // Dirichlet + if (cimg::abs(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 (deltaz) // Shift along Z-axis - switch (border_condition) { - case 0 : - if (cimg::abs(deltaz)>=depth()) return fill(0); - if (deltaz<0) cimg_forC(*this,c) { - std::memmove(data(0,0,0,c),data(0,0,-deltaz,c),_width*_height*(_depth+deltaz)*sizeof(T)); - std::memset(data(0,0,_depth+deltaz,c),0,_width*_height*(-deltaz)*sizeof(T)); - } else cimg_forC(*this,c) { - std::memmove(data(0,0,deltaz,c),data(0,0,0,c),_width*_height*(_depth-deltaz)*sizeof(T)); - std::memset(data(0,0,0,c),0,deltaz*_width*_height*sizeof(T)); - } - break; - case 1 : - if (deltaz<0) { - const int ndeltaz = (-deltaz>=depth())?_depth-1:-deltaz; - if (!ndeltaz) return *this; + 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 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,ndeltaz,c),_width*_height*(_depth-ndeltaz)*sizeof(T)); - T *ptrd = data(0,0,_depth-ndeltaz,c), *ptrs = data(0,0,_depth-1,c); - for (int l = 0; l=depth())?_depth-1:deltaz; - if (!ndeltaz) return *this; + 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,ndeltaz,c),data(0,0,0,c),_width*_height*(_depth-ndeltaz)*sizeof(T)); + 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; l0) cimg_forC(*this,c) { - std::memcpy(buf,data(0,0,0,c),_width*_height*ndeltaz*sizeof(T)); - std::memmove(data(0,0,0,c),data(0,0,ndeltaz,c),_width*_height*(_depth-ndeltaz)*sizeof(T)); - std::memcpy(data(0,0,_depth-ndeltaz,c),buf,_width*_height*ndeltaz*sizeof(T)); - } else cimg_forC(*this,c) { - std::memcpy(buf,data(0,0,_depth+ndeltaz,c),-ndeltaz*_width*_height*sizeof(T)); - std::memmove(data(0,0,-ndeltaz,c),data(0,0,0,c),_width*_height*(_depth+ndeltaz)*sizeof(T)); - std::memcpy(data(0,0,0,c),buf,-ndeltaz*_width*_height*sizeof(T)); - } - delete[] buf; - } + default : // Dirichlet + if (cimg::abs(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 (deltac) // Shift along C-axis - switch (border_condition) { - case 0 : - if (cimg::abs(deltac)>=spectrum()) return fill(0); - if (deltac<0) { - std::memmove(_data,data(0,0,0,-deltac),_width*_height*_depth*(_spectrum+deltac)*sizeof(T)); - std::memset(data(0,0,0,_spectrum+deltac),0,_width*_height*_depth*(-deltac)*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 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::memmove(data(0,0,0,deltac),_data,_width*_height*_depth*(_spectrum-deltac)*sizeof(T)); - std::memset(_data,0,deltac*_width*_height*_depth*sizeof(T)); + 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 : - if (deltac<0) { - const int ndeltac = (-deltac>=spectrum())?_spectrum-1:-deltac; - if (!ndeltac) return *this; - std::memmove(_data,data(0,0,0,ndeltac),_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T)); - T *ptrd = data(0,0,0,_spectrum-ndeltac), *ptrs = data(0,0,0,_spectrum-1); - for (int l = 0; l=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=spectrum())?_spectrum-1:deltac; - if (!ndeltac) return *this; - std::memmove(data(0,0,0,ndeltac),_data,_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T)); + 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; l0) { - std::memcpy(buf,_data,_width*_height*_depth*ndeltac*sizeof(T)); - std::memmove(_data,data(0,0,0,ndeltac),_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T)); - std::memcpy(data(0,0,0,_spectrum-ndeltac),buf,_width*_height*_depth*ndeltac*sizeof(T)); + default : // Dirichlet + if (cimg::abs(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::memcpy(buf,data(0,0,0,_spectrum+ndeltac),-ndeltac*_width*_height*_depth*sizeof(T)); - std::memmove(data(0,0,0,-ndeltac),_data,_width*_height*_depth*(_spectrum+ndeltac)*sizeof(T)); - std::memcpy(_data,buf,-ndeltac*_width*_height*_depth*sizeof(T)); + 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)); } - delete[] buf; - } } return *this; } - CImg get_shift(const int deltax, const int deltay=0, const int deltaz=0, const int deltac=0, - const int border_condition=0) const { - return (+*this).shift(deltax,deltay,deltaz,deltac,border_condition); - } - - // Permute axes order (internal). - template - CImg _get_permute_axes(const char *const permut, const t&) const { - if (is_empty() || !permut) return CImg(*this,false); - CImg res; - const T* ptrs = _data; - if (!cimg::strncasecmp(permut,"xyzc",4)) return (+*this); - if (!cimg::strncasecmp(permut,"xycz",4)) { - res.assign(_width,_height,_spectrum,_depth); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"xzyc",4)) { - res.assign(_width,_depth,_height,_spectrum); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"xzcy",4)) { - res.assign(_width,_depth,_spectrum,_height); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"xcyz",4)) { - res.assign(_width,_spectrum,_height,_depth); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"xczy",4)) { - res.assign(_width,_spectrum,_depth,_height); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"yxzc",4)) { - res.assign(_height,_width,_depth,_spectrum); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"yxcz",4)) { - res.assign(_height,_width,_spectrum,_depth); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"yzxc",4)) { - res.assign(_height,_depth,_width,_spectrum); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"yzcx",4)) { - 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++); *(ptr_g++) = (t)*(ptrs++); - } - } 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++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); - } - } 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++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); *(ptr_a++) = (t)*(ptrs++); - } - } break; - default : { - const unsigned long wh = (unsigned long)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; - } - } - } - if (!cimg::strncasecmp(permut,"ycxz",4)) { - res.assign(_height,_spectrum,_width,_depth); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"yczx",4)) { - res.assign(_height,_spectrum,_depth,_width); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"zxyc",4)) { - res.assign(_depth,_width,_height,_spectrum); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"zxcy",4)) { - res.assign(_depth,_width,_spectrum,_height); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"zyxc",4)) { - res.assign(_depth,_height,_width,_spectrum); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"zycx",4)) { - res.assign(_depth,_height,_spectrum,_width); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"zcxy",4)) { - res.assign(_depth,_spectrum,_width,_height); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"zcyx",4)) { - res.assign(_depth,_spectrum,_height,_width); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"cxyz",4)) { - res.assign(_spectrum,_width,_height,_depth); - switch (_spectrum) { - case 1 : { - const T *ptr_r = data(0,0,0,0); - t *ptrd = res._data; - for (unsigned int siz = _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 (unsigned int siz = _width*_height*_depth; siz; --siz) { - *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); - } - } 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 (unsigned int siz = _width*_height*_depth; siz; --siz) { - *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); - } - } 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 (unsigned int siz = _width*_height*_depth; siz; --siz) { - *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++); - } - } break; - default : { - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); - } - } - } - if (!cimg::strncasecmp(permut,"cxzy",4)) { - res.assign(_spectrum,_width,_depth,_height); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"cyxz",4)) { - res.assign(_spectrum,_height,_width,_depth); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"cyzx",4)) { - res.assign(_spectrum,_height,_depth,_width); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"czxy",4)) { - res.assign(_spectrum,_depth,_width,_height); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); - } - if (!cimg::strncasecmp(permut,"czyx",4)) { - res.assign(_spectrum,_depth,_height,_width); - const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); - } - if (!res) - throw CImgArgumentException(_cimg_instance - "permute_axes() : Invalid specified permutation '%s'.", - cimg_instance, - permut); - return res; + //! Shift image content \newinstance. + CImg 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. /** - This function permutes image axes. - \param permut = String describing the permutation (4 characters). + \param order Axes permutations, as a C-string of 4 characters. + This function permutes image content regarding the specified axes permutation. **/ CImg& permute_axes(const char *const order) { return get_permute_axes(order).move_to(*this); } + //! Permute axes order \newinstance. CImg get_permute_axes(const char *const order) const { const T foo = (T)0; - return _get_permute_axes(order,foo); + return _permute_axes(order,foo); } - //! Unroll all images values into specified axis. + template + CImg _permute_axes(const char *const order, const t&) const { + if (is_empty() || !order) return CImg(*this,false); + CImg 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& unroll(const char axis) { - const unsigned int siz = size(); - if (siz) switch (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; @@ -20597,558 +30544,1309 @@ namespace cimg_library { return *this; } + //! Unroll pixel values along specified axis \newinstance. CImg get_unroll(const char axis) const { return (+*this).unroll(axis); } - //! Rotate an image. + //! Rotate image with arbitrary angle. /** - \param angle = rotation angle (in degrees). - \param cond = rotation type. can be : - - 0 = zero-value at borders - - 1 = nearest pixel. - - 2 = cyclic. - \note Returned image will probably have a different size than the image instance *this. + \param angle Rotation angle, in degrees. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note The size of the image is modified. **/ - CImg& rotate(const float angle, const unsigned int border_conditions=0, const unsigned int interpolation=1) { - return get_rotate(angle,border_conditions,interpolation).move_to(*this); + CImg& rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) { + const float nangle = cimg::mod(angle,360.0f); + if (nangle==0.0f) return *this; + return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this); } - CImg get_rotate(const float angle, const unsigned int border_conditions=0, const unsigned int interpolation=1) const { + //! Rotate image with arbitrary angle \newinstance. + CImg get_rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) const { if (is_empty()) return *this; CImg res; const float nangle = cimg::mod(angle,360.0f); - if (border_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles + if (boundary_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // Optimized version for orthogonal angles. const int wm1 = width() - 1, hm1 = height() - 1; const int iangle = (int)nangle/90; switch (iangle) { - case 1 : { + 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); + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c); } break; - case 2 : { + 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); + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c); } break; - case 3 : { + 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); + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c); } break; - default : + default : // 0 deg return *this; } - } else { // generic version - const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + } else { // Generic angle const float rad = (float)(nangle*cimg::PI/180.0), - ca = (float)std::cos(rad), - sa = (float)std::sin(rad), - ux = cimg::abs(_width*ca), uy = cimg::abs(_width*sa), - vx = cimg::abs(_height*sa), vy = cimg::abs(_height*ca), - w2 = 0.5f*_width, h2 = 0.5f*_height, - dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy); - res.assign((int)(ux+vx),(int)(uy+vy),_depth,_spectrum); - switch (border_conditions) { - case 0 : { - switch (interpolation) { - case 2 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { - const Tfloat val = cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0); - res(x,y,z,c) = (T)(valvmax?vmax:val); - } - } break; - case 1 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0); - } break; - default : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c,0); - } - } - } break; - case 1 : { - switch (interpolation) { - case 2 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { - const Tfloat val = _cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c); - res(x,y,z,c) = (T)(valvmax?vmax:val); - } - } break; - case 1 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (T)_linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c); - } break; - default : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = _atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c); - } - } - } break; - case 2 : { - switch (interpolation) { - case 2 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { - const Tfloat val = _cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()), - cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c); - res(x,y,z,c) = (T)(valvmax?vmax:val); - } - } break; - case 1 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()), - cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c); - } break; - default : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width()), - cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height()),z,c); - } - } - } break; - default : - throw CImgArgumentException(_cimg_instance - "rotate() : Invalid specified border conditions %d " - "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).", - cimg_instance, - border_conditions); - } + 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 an image around a center point (\c cx,\c cy). + //! 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 zoom = zoom. - \param cond = rotation type. can be : - - 0 = zero-value at borders - - 1 = repeat image at borders - - 2 = zero-value at borders and linear interpolation + \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, { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions, { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. **/ - CImg& rotate(const float angle, const float cx, const float cy, const float zoom, - const unsigned int border_conditions=3, const unsigned int interpolation=1) { - return get_rotate(angle,cx,cy,zoom,border_conditions,interpolation).move_to(*this); + CImg& 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); } - CImg get_rotate(const float angle, const float cx, const float cy, const float zoom, - const unsigned int border_conditions=3, const unsigned int interpolation=1) const { - if (interpolation>2) - throw CImgArgumentException(_cimg_instance - "rotate() : Invalid specified interpolation %d " - "(should be { 0=none | 1=linear | 2=bicubic }).", - cimg_instance, - interpolation); - + //! Rotate image with arbitrary angle, around a center point \newinstance. + CImg 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 res(_width,_height,_depth,_spectrum); - const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); - const float - rad = (float)((angle*cimg::PI)/180.0), - ca = (float)std::cos(rad)/zoom, - sa = (float)std::sin(rad)/zoom; - switch (border_conditions) { - case 0 : { - switch (interpolation) { - case 2 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { - const Tfloat val = cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0); - res(x,y,z,c) = (T)(valvmax?vmax:val); - } - } break; - case 1 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (T)linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0); - } break; - default : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c,0); - } - } - } break; - case 1 : { - switch (interpolation) { - case 2 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { - const Tfloat val = _cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c); - res(x,y,z,c) = (T)(valvmax?vmax:val); - } - } break; - case 1 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (T)_linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c); - } break; - default : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = _atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c); - } - } - } break; - case 2 : { - switch (interpolation) { - case 2 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) { - const Tfloat val = _cubic_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()), - cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c); - res(x,y,z,c) = (T)(valvmax?vmax:val); - } - } break; - case 1 : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (T)_linear_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()), - cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c); - } break; - default : { - cimg_forXY(res,x,y) cimg_forZC(*this,z,c) - res(x,y,z,c) = (*this)(cimg::mod((int)(cx + (x-cx)*ca + (y-cy)*sa),width()), - cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),height()),z,c); - } - } - } break; - default : - throw CImgArgumentException(_cimg_instance - "rotate() : Invalid specified border conditions %d " - "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).", - cimg_instance, - border_conditions); - } + _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy); return res; } - //! Warp an image. - template - CImg& warp(const CImg& warp, const bool is_relative=false, - const bool interpolation=true, const unsigned int border_conditions=0) { - return get_warp(warp,is_relative,interpolation,border_conditions).move_to(*this); + // [internal] Perform 2d rotation with arbitrary angle. + void _rotate(CImg& 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.0), + 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.0f*width(), hh = 2.0f*height(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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); + const Tfloat val = _cubic_atXY(mx::cut(val); + } + } break; + case 1 : { // Linear interpolation + const float ww = 2.0f*width(), hh = 2.0f*height(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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=2048)) + cimg_forXYZC(res,x,y,z,c) { + const float xc = x - rw2, yc = y - rh2; + const Tfloat val = _cubic_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()), + cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c); + res(x,y,z,c) = cimg::type::cut(val); + } + } break; + case 1 : { // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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(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(res.size()>=2048)) + cimg_forXYZC(res,x,y,z,c) { + const float xc = x - rw2, yc = y - rh2; + const Tfloat val = _cubic_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c); + res(x,y,z,c) = cimg::type::cut(val); + } + } break; + case 1 : { // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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(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(res.size()>=2048)) + cimg_forXYZC(res,x,y,z,c) { + const float xc = x - rw2, yc = y - rh2; + const Tfloat val = cubic_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0); + res(x,y,z,c) = cimg::type::cut(val); + } + } break; + case 1 : { // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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(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 { 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note Most of the time, size of the image is modified. + **/ + CImg 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.0f); + if (nangle==0.0f) 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 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 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 R = CImg::rotation_matrix(u,v,w,angle); + const CImg + X = R*CImg(8,3,1,1, + 0.0f,w1,w1,0.0f,0.0f,w1,w1,0.0f, + 0.0f,0.0f,h1,h1,0.0f,0.0f,h1,h1, + 0.0f,0.0f,0.0f,0.0f,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 { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic }. + \note Most of the time, size of the image is modified. + **/ + CImg 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.0f); + if (nangle==0.0f) 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 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 res(_width,_height,_depth,_spectrum); + CImg R = CImg::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& res, const CImg& 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.0f*width(), hh = 2.0f*height(), dd = 2.0f*depth(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(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) = cimg::type::cut(_cubic_atXYZ(X=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=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=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) = cimg::type::cut(_cubic_atXYZ(X,Y,Z,c)); + } + } break; + case 1 : { // Linear interpolation + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(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(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(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) = cimg::type::cut(_cubic_atXYZ(X,Y,Z,c)); + } + } break; + case 1 : { // Linear interpolation + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(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(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(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) = cimg::type::cut(cubic_atXYZ(X,Y,Z,c,(T)0)); + } + } break; + case 1 : { // Linear interpolation + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(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(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 { 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ template - CImg get_warp(const CImg& warp, const bool is_relative=false, - const bool interpolation=true, const unsigned int border_conditions=0) const { + CImg& warp(const CImg& 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 + CImg get_warp(const CImg& 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 (is_relative && !is_sameXYZ(warp)) + if (mode && !is_sameXYZ(warp)) throw CImgArgumentException(_cimg_instance - "warp() : Instance and specified relative warping field (%u,%u,%u,%u,%p) " + "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 res(warp._width,warp._height,warp._depth,_spectrum); - T *ptrd = res._data; - switch (warp._spectrum) { - case 1 : // 1d warping. - if (is_relative) { // Relative warp coordinates - if (interpolation) switch (border_conditions) { - case 2 : { - cimg_forC(res,c) { - const t *ptrs0 = warp._data; - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); - } - } break; - case 1 : { - cimg_forC(res,c) { - const t *ptrs0 = warp._data; - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c); - } - } break; - default : { - cimg_forC(res,c) { - const t *ptrs0 = warp._data; - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,0); + + 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(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=1) // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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)_cubic_atX(mx=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++) = (T)_cubic_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); } - } break; - default : { - cimg_forC(res,c) { - const t *ptrs0 = warp._data; - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,0); + break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)_cubic_atX(x - (float)*(ptrs0++),y,z,c); + } + break; + default : // Dirichlet + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)cubic_atX(x - (float)*(ptrs0++),y,z,c,(T)0); } } - } else switch (border_conditions) { - case 2 : { - cimg_forC(res,c) { - const t *ptrs0 = warp._data; - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),0,0,c); + else if (interpolation==1) // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float w2 = 2.0f*width(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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; - default : { - cimg_forC(res,c) { - const t *ptrs0 = warp._data; - cimg_forXYZ(res,x,y,z) - *(ptrd++) = atX((int)*(ptrs0++),0,0,c,0); + break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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(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(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=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)_cubic_atX(mx=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++) = (T)_cubic_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); } - } break; - default : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0); + break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)_cubic_atX((float)*(ptrs0++),0,0,c); + } + break; + default : // Dirichlet + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)cubic_atX((float)*(ptrs0++),0,0,c,(T)0); } } - } else switch (border_conditions) { - case 2 : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width), - cimg::mod(y - (int)*(ptrs1++),(int)_height),z,c); + else if (interpolation==1) // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float w2 = 2.0f*width(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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; - default : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0); + break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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(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 { // Absolute warp coordinates - if (interpolation) switch (border_conditions) { - case 2 : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width), - cimg::mod((float)*(ptrs1++),(float)_height),0,c); + 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(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=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=0 && Y=1) // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=0 && Y=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)_cubic_atXY(mx=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++) = (T)_cubic_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(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++) = (T)_cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); + } + break; + default : // Dirichlet + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)cubic_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.0f*width(), h2 = 2.0f*height(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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(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(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(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=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)_cubic_atXY(mx=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++) = (T)_cubic_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(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++) = (T)_cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); + } + break; + default : // Dirichlet + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)cubic_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.0f*width(), h2 = 2.0f*height(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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(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(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(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=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=0 && Y=0 && Z=1) // Linear interpolation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=0 && Y=0 && Z=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)_cubic_atXYZ(mx=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++) = (T)_cubic_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(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++) = (T)_cubic_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c); + } + break; + default : // Dirichlet + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)cubic_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.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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(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 : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0); + break; + default : // Dirichlet + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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 switch (border_conditions) { - case 2 : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width), - cimg::mod(y - (int)*(ptrs1++),(int)_height), - cimg::mod(z - (int)*(ptrs2++),(int)_depth),c); + 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(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=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)_cubic_atXYZ(mx=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++) = (T)_cubic_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width), + cimg::mod((float)*(ptrs1++),(float)_height), + cimg::mod((float)*(ptrs2++),(float)_depth),c); } - } break; - default : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0); + break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)_cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); + } + break; + default : // Dirichlet + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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++) = (T)cubic_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++), + c,(T)0); } } - } else switch (border_conditions) { - case 2 : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width), - cimg::mod((int)*(ptrs1++),(int)_height), - cimg::mod((int)*(ptrs2++),(int)_depth),c); + else if (interpolation==1) // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth(); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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=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; - default : { - cimg_forC(res,c) { - const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2); - cimg_forXYZ(res,x,y,z) - *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,0); + break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(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(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(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& 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); - } - + //! 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 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 @@ -21156,15 +31854,22 @@ namespace cimg_library { _y0 = (y0>=_height)?_height - 1:y0, _z0 = (z0>=_depth)?_depth - 1:z0; const CImg - 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); + 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(_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); } - //! Get a square region of the image. + //! Construct a 2d representation of a 3d image, with XY,XZ and YZ views \inplace. + CImg& 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. @@ -21174,209 +31879,241 @@ namespace cimg_library { \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 border_condition = Dirichlet (false) or Neumann border conditions. + \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. **/ CImg& 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 bool border_condition=false) { - return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,border_condition).move_to(*this); + 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 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 bool border_condition=false) const { - if (is_empty()) return *this; + const unsigned int boundary_conditions=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "crop(): Empty instance.", + cimg_instance); const int nx0 = x0 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()) { - if (border_condition) cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0+x,ny0+y,nz0+z,nc0+c); - else res.fill(0).draw_image(-nx0,-ny0,-nz0,-nc0,*this); - } else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this); + 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>=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=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>=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; } - //! Get a rectangular part of the image instance. - /** - \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 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 border_condition = determine the type of border condition if - some of the desired region is outside the image. - **/ + //! Crop image region \overloading. CImg& crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, - const bool border_condition=false) { - return crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,border_condition); + const unsigned int boundary_conditions=0) { + return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); } + //! Crop image region \newinstance. CImg get_crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, - const bool border_condition=false) const { - return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,border_condition); + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); } - //! Get a rectangular part of the image instance. - /** - \param x0 = X-coordinate of the upper-left crop rectangle corner. - \param y0 = Y-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 border_condition = determine the type of border condition if - some of the desired region is outside the image. - **/ + //! Crop image region \overloading. CImg& crop(const int x0, const int y0, const int x1, const int y1, - const bool border_condition=false) { - return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,border_condition); + 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 get_crop(const int x0, const int y0, const int x1, const int y1, - const bool border_condition=false) const { - return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,border_condition); + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); } - //! Get a rectangular part of the image instance. - /** - \param x0 = X-coordinate of the upper-left crop rectangle corner. - \param x1 = X-coordinate of the lower-right crop rectangle corner. - \param border_condition = determine the type of border condition if - some of the desired region is outside the image. - **/ - CImg& crop(const int x0, const int x1, const bool border_condition=false) { - return crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,border_condition); + //! Crop image region \overloading. + CImg& 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); } - CImg get_crop(const int x0, const int x1, const bool border_condition=false) const { - return get_crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,border_condition); + //! Crop image region \newinstance. + CImg 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 an image, regarding of the specified backround value. - CImg& autocrop(const T value, const char *const axes="czyx") { + //! Autocrop image region, regarding the specified background value. + CImg& 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::uncase(*s); + const char axis = cimg::lowercase(*s); const CImg coords = _autocrop(value,axis); - switch (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); + 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); + 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); + if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1); } } } return *this; } - CImg get_autocrop(const T value, const char *const axes="czyx") const { + //! Autocrop image region, regarding the specified background value \newinstance. + CImg get_autocrop(const T& value, const char *const axes="czyx") const { return (+*this).autocrop(value,axes); } - //! Autocrop an image, regarding of the specified backround color. - CImg& autocrop(const T *const color, const char *const axes="zyx") { + //! 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& autocrop(const T *const color=0, const char *const axes="zyx") { if (is_empty()) return *this; + if (!color) { // Guess color. + const CImg 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 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::uncase(*s); + const char axis = cimg::lowercase(*s); switch (axis) { case 'x' : { - int x0 = _width, x1 = -1; + int x0 = width(), x1 = -1; cimg_forC(*this,c) { const CImg coords = get_shared_channel(c)._autocrop(color[c],'x'); const int nx0 = coords[0], nx1 = coords[1]; - if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); } + if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); } } - if (x0<=x1) crop(x0,x1); + if (x0==width() && x1==-1) return assign(); else crop(x0,x1); } break; case 'y' : { - int y0 = _height, y1 = -1; + int y0 = height(), y1 = -1; cimg_forC(*this,c) { const CImg coords = get_shared_channel(c)._autocrop(color[c],'y'); const int ny0 = coords[0], ny1 = coords[1]; - if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); } + if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); } } - if (y0<=y1) crop(0,y0,_width-1,y1); + if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1); } break; default : { - int z0 = _depth, z1 = -1; + int z0 = depth(), z1 = -1; cimg_forC(*this,c) { const CImg coords = get_shared_channel(c)._autocrop(color[c],'z'); const int nz0 = coords[0], nz1 = coords[1]; - if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); } + if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); } } - if (z0<=z1) crop(0,0,z0,_width-1,_height-1,z1); + if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1); } } } return *this; } - CImg get_autocrop(const T *const color, const char *const axes="zyx") const { + //! Autocrop image region, regarding the specified background color \newinstance. + CImg get_autocrop(const T *const color=0, const char *const axes="zyx") const { return (+*this).autocrop(color,axes); } - //! Autocrop an image, regarding of the specified backround color. + //! Autocrop image region, regarding the specified background color \overloading. template CImg& autocrop(const CImg& color, const char *const axes="zyx") { return get_autocrop(color,axes).move_to(*this); } + //! Autocrop image region, regarding the specified background color \newinstance. template CImg get_autocrop(const CImg& color, const char *const axes="zyx") const { return get_autocrop(color._data,axes); } - CImg _autocrop(const T value, const char axis) const { + CImg _autocrop(const T& value, const char axis) const { CImg res; - int x0 = -1, y0 = -1, z0 = -1, c0 = -1, x1 = -1, y1 = -1, z1 = -1, c1 = -1; - switch (cimg::uncase(axis)) { + 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) + 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::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) + 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::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) + 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::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) + 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::vector(c0,c1); @@ -21385,318 +32122,891 @@ namespace cimg_library { return res; } - //! Get one column. - CImg& column(const unsigned int x0) { - return columns(x0,x0); - } - - CImg get_column(const unsigned int x0) const { + //! Return specified image column. + /** + \param x0 Image column. + **/ + CImg get_column(const int x0) const { return get_columns(x0,x0); } - //! Get a set of columns. - CImg& columns(const unsigned int x0, const unsigned int x1) { + //! Return specified image column \inplace. + CImg& 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& columns(const int x0, const int x1) { return get_columns(x0,x1).move_to(*this); } - CImg get_columns(const unsigned int x0, const unsigned int x1) const { - return get_crop((int)x0,0,0,0,(int)x1,height()-1,depth()-1,spectrum()-1); + //! Return specified range of image columns \inplace. + CImg get_columns(const int x0, const int x1) const { + return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1); } - //! Get a line. - CImg& line(const unsigned int y0) { - return lines(y0,y0); + //! Return specified image row. + CImg get_row(const int y0) const { + return get_rows(y0,y0); } - CImg get_line(const unsigned int y0) const { - return get_lines(y0,y0); + //! Return specified image row \inplace. + /** + \param y0 Image row. + **/ + CImg& row(const int y0) { + return rows(y0,y0); } - //! Get a set of lines. - CImg& lines(const unsigned int y0, const unsigned int y1) { - return get_lines(y0,y1).move_to(*this); + //! Return specified range of image rows. + /** + \param y0 Starting image row. + \param y1 Ending image row. + **/ + CImg get_rows(const int y0, const int y1) const { + return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1); } - CImg get_lines(const unsigned int y0, const unsigned int y1) const { - return get_crop(0,(int)y0,0,0,width()-1,(int)y1,depth()-1,spectrum()-1); + //! Return specified range of image rows \inplace. + CImg& rows(const int y0, const int y1) { + return get_rows(y0,y1).move_to(*this); } - //! Get a slice. - CImg& slice(const unsigned int z0) { - return slices(z0,z0); - } - - CImg get_slice(const unsigned int z0) const { + //! Return specified image slice. + /** + \param z0 Image slice. + **/ + CImg get_slice(const int z0) const { return get_slices(z0,z0); } - //! Get a set of slices. - CImg& slices(const unsigned int z0, const unsigned int z1) { + //! Return specified image slice \inplace. + CImg& 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 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& slices(const int z0, const int z1) { return get_slices(z0,z1).move_to(*this); } - CImg get_slices(const unsigned int z0, const unsigned int z1) const { - return get_crop(0,0,(int)z0,0,width()-1,height()-1,(int)z1,spectrum()-1); - } - - //! Get a channel. - CImg& channel(const unsigned int c0) { - return channels(c0,c0); - } - - CImg get_channel(const unsigned int c0) const { + //! Return specified image channel. + /** + \param c0 Image channel. + **/ + CImg get_channel(const int c0) const { return get_channels(c0,c0); } - //! Get a set of channels. - CImg& channels(const unsigned int c0, const unsigned int c1) { + //! Return specified image channel \inplace. + CImg& 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 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& channels(const int c0, const int c1) { return get_channels(c0,c1).move_to(*this); } - CImg get_channels(const unsigned int c0, const unsigned int c1) const { - return get_crop(0,0,0,(int)c0,width()-1,height()-1,depth()-1,(int)c1); + //! Return stream line of a 2d or 3d vector field. + CImg 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::_functor4d_streamline2d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.0f,_height - 1.0f,0.0f); + } else { + typename CImg::_functor4d_streamline2d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.0f,_height - 1.0f,0.0f); + } + } + if (is_oriented_only) { + typename CImg::_functor4d_streamline3d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f); + } + typename CImg::_functor4d_streamline3d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f); } - //! Get a shared-memory image referencing a set of points of the image instance. + //! 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 { 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }. + \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 + static CImg 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 && (xx1 || yy1 || zz1))) return CImg(); + const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1); + CImg 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 && (Xx1 || Yy1 || Zz1)) 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 && (Xx1 || Yy1 || Zz1)) 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 && (Xx1 || Yy1 || Zz1)) 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 && (Xx1 || Yy1 || Zz1)) 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 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& ref; + _functor4d_streamline2d_directed(const CImg& 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& ref; + _functor4d_streamline3d_directed(const CImg& 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& ref; + CImg *pI; + _functor4d_streamline2d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(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& 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& ref; + CImg *pI; + _functor4d_streamline3d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(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& 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::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 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 = offset(x1,y0,z0,c0); + 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).", + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", cimg_instance, x0,x1,y0,z0,c0); - return CImg(_data+beg,x1-x0+1,1,1,1,true); + return CImg(_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 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 = offset(x1,y0,z0,c0); + 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).", + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", cimg_instance, x0,x1,y0,z0,c0); - return CImg(_data+beg,x1-x0+1,1,1,1,true); + return CImg(_data + beg,x1 - x0 + 1,1,1,1,true); } - //! Return a shared-memory image referencing a set of lines of the image instance. - CImg get_shared_lines(const unsigned int y0, const unsigned int y1, + //! 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 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 = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0); + 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_lines() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).", + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", cimg_instance, - _width-1,y0,y1,z0,c0); + _width - 1,y0,y1,z0,c0); - return CImg(_data+beg,_width,y1-y0+1,1,1,true); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); } - const CImg get_shared_lines(const unsigned int y0, const unsigned int y1, + //! Return a shared-memory image referencing a range of rows of the image instance \const. + const CImg 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 = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0); + 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_lines() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).", + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", cimg_instance, - _width-1,y0,y1,z0,c0); + _width - 1,y0,y1,z0,c0); - return CImg(_data+beg,_width,y1-y0+1,1,1,true); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); } - //! Return a shared-memory image referencing one particular line (y0,z0,c0) of the image instance. - CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) { - return get_shared_lines(y0,y0,z0,c0); + //! 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 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); } - const CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const { - return get_shared_lines(y0,y0,z0,c0); + //! Return a shared-memory image referencing one row of the image instance \const. + const CImg 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 set of planes (z0->z1,c0) of the image instance. - CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) { - const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,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 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_planes() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).", + "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); + _width - 1,_height - 1,z0,z1,c0); - return CImg(_data+beg,_width,_height,z1-z0+1,1,true); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); } - const CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const { - const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0); + //! Return a shared memory image referencing a range of slices of the image instance \const. + const CImg 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_planes() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).", + "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); + _width - 1,_height - 1,z0,z1,c0); - return CImg(_data+beg,_width,_height,z1-z0+1,1,true); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); } - //! Return a shared-memory image referencing one plane (z0,c0) of the image instance. - CImg get_shared_plane(const unsigned int z0, const unsigned int c0=0) { - return get_shared_planes(z0,z0,c0); + //! Return a shared-memory image referencing one slice of the image instance. + /** + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) { + return get_shared_slices(z0,z0,c0); } - const CImg get_shared_plane(const unsigned int z0, const unsigned int c0=0) const { - return get_shared_planes(z0,z0,c0); + //! Return a shared-memory image referencing one slice of the image instance \const. + const CImg 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 set of channels (c0->c1) of the image instance. + //! 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 get_shared_channels(const unsigned int c0, const unsigned int c1) { - const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,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).", + "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); + _width - 1,_height - 1,_depth - 1,c0,c1); - return CImg(_data+beg,_width,_height,_depth,c1-c0+1,true); + return CImg(_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 get_shared_channels(const unsigned int c0, const unsigned int c1) const { - const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,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).", + "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); + _width - 1,_height - 1,_depth - 1,c0,c1); - return CImg(_data+beg,_width,_height,_depth,c1-c0+1,true); + return CImg(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); } - //! Return a shared-memory image referencing one channel c0 of the image instance. + //! Return a shared-memory image referencing one channel of the image instance. + /** + \param c0 C-coordinate. + **/ CImg 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 get_shared_channel(const unsigned int c0) const { return get_shared_channels(c0,c0); } - //! Return a shared version of the image instance. + //! Return a shared-memory version of the image instance. CImg get_shared() { return CImg(_data,_width,_height,_depth,_spectrum,true); } + //! Return a shared-memory version of the image instance \const. const CImg get_shared() const { return CImg(_data,_width,_height,_depth,_spectrum,true); } - //! Split image into a list. - CImgList get_split(const char axis, const int nb=0) const { + //! Split image into a list along specified axis. + /** + \param axis Splitting axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \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 get_split(const char axis, const int nb=-1) const { CImgList res; - const char naxis = cimg::uncase(axis); - const unsigned int nnb = (unsigned int)(nb==0?1:(nb>0?nb:-nb)); - if (nb<=0) switch (naxis) { // Split by bloc size - case 'x': { - for (unsigned int p = 0; p<_width; p+=nnb) get_crop(p,0,0,0,cimg::min(p+nnb-1,_width-1),_height-1,_depth-1,_spectrum-1).move_to(res); - } break; - case 'y': { - for (unsigned int p = 0; p<_height; p+=nnb) get_crop(0,p,0,0,_width-1,cimg::min(p+nnb-1,_height-1),_depth-1,_spectrum-1).move_to(res); - } break; - case 'z': { - for (unsigned int p = 0; p<_depth; p+=nnb) get_crop(0,0,p,0,_width-1,_height-1,cimg::min(p+nnb-1,_depth-1),_spectrum-1).move_to(res); - } break; - default: { - for (unsigned int p = 0; p<_spectrum; p+=nnb) get_crop(0,0,0,p,_width-1,_height-1,_depth-1,cimg::min(p+nnb-1,_spectrum-1)).move_to(res); - } - } else { // Split by number of blocs - const unsigned int siz = naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:naxis=='c'?_spectrum:0; - if (nnb>siz) + 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>=128 && _height*_depth*_spectrum>=128)) + for (unsigned int p = 0; pdp) { + 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>=128 && _width*_depth*_spectrum>=128)) + for (unsigned int p = 0; pdp) { + 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>=128 && _width*_height*_spectrum>=128)) + for (unsigned int p = 0; pdp) { + 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>=128 && _width*_height*_depth>=128)) + for (unsigned int p = 0; p0) { // 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.", + "get_split(): Instance cannot be split along %c-axis into %u blocs.", cimg_instance, - axis,nnb); - res.assign(nnb); - switch (naxis) { + 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' : { - cimglist_for(res,p) get_crop(p*siz/nnb,0,0,0,(p+1)*siz/nnb-1,_height-1,_depth-1,_spectrum-1).move_to(res[p]); + 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' : { - cimglist_for(res,p) get_crop(0,p*siz/nnb,0,0,_width-1,(p+1)*siz/nnb-1,_depth-1,_spectrum-1).move_to(res[p]); + 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' : { - cimglist_for(res,p) get_crop(0,0,p*siz/nnb,0,_width-1,_height-1,(p+1)*siz/nnb-1,_spectrum-1).move_to(res[p]); + 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 : { - cimglist_for(res,p) get_crop(0,0,0,p*siz/nnb,_width-1,_height-1,_depth-1,(p+1)*siz/nnb-1).move_to(res[p]); + longT i0 = 0; + cimg_foroff(*this,i) + if ((*this)[i]!=current) { + CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); + i0 = (longT)i; current = (*this)[i]; + } + CImg(_data + i0,1,(unsigned int)(size() - i0)).move_to(res); } } } return res; } - //! Split image into a list of one-column vectors, according to specified splitting value. - CImgList get_split(const T value, const bool keep_values, const bool is_shared) const { - CImgList res; - for (const T *ps = _data, *_ps = ps, *const pe = end(); ps(ps,1,siz,1,1,is_shared),~0U,is_shared); - ps = _ps; - while (_ps(ps,1,siz,1,1,is_shared),~0U,is_shared); - ps = _ps; - } - return res; - } - - //! Split image into a list of one-column vectors, according to specified sequence of splitting values. + //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis. /** - \param values The splitting pattern of values. - \param keep_values Can be : - - false : Discard splitting values in resulting list. - - true : Keep splitting values as separate images in resulting list. + \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 - CImgList get_split(const CImg& values, const bool keep_values, const bool is_shared) const { - if (!values) return CImgList(*this); - if (values.size()==1) return get_split(*values,keep_values,is_shared); + CImgList get_split(const CImg& values, const char axis=0, const bool keep_values=true) const { CImgList res; - const t *pve = values.end(); - for (const T *ps = _data, *_ps = ps, *const pe = end(); ps(ps,1,siz,1,1,is_shared),~0U,is_shared); // If match found. - ps = _ps; - - // Try to find non-match from current position. - do { - pv = values._data; - while (_ps(ps,1,siz,1,1,is_shared),~0U,is_shared); - ps = _ps; + if (is_empty()) return res; + const ulongT vsiz = values.size(); + const char _axis = cimg::lowercase(axis); + if (!vsiz) return CImgList(*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 (ii0) { if (keep_values) CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; } + while (ii0) { CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; } + } while (i=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=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) CImg(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res); + if (keep_values) CImg(_data + i1,1,(unsigned int)(i - i1)).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i(_data + i0,1,(unsigned int)(siz - i0)).move_to(res); + } break; + } } return res; } - //! Append an image. + //! Append two images along specified axis. + /** + \param img Image to append with instance image. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Append alignment in \c [0,1]. + **/ template CImg& append(const CImg& img, const char axis='x', const float align=0) { if (is_empty()) return assign(img,false); @@ -21704,12 +33014,14 @@ namespace cimg_library { return CImgList(*this,true).insert(img).get_append(axis,align).move_to(*this); } + //! Append two images along specified axis \specialization. CImg& append(const CImg& img, const char axis='x', const float align=0) { if (is_empty()) return assign(img,false); if (!img) return *this; return CImgList(*this,img,true).get_append(axis,align).move_to(*this); } + //! Append two images along specified axis \const. template CImg<_cimg_Tt> get_append(const CImg& img, const char axis='x', const float align=0) const { if (is_empty()) return +img; @@ -21717,6 +33029,7 @@ namespace cimg_library { return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align); } + //! Append two images along specified axis \specialization. CImg get_append(const CImg& img, const char axis='x', const float align=0) const { if (is_empty()) return +img; if (!img) return +*this; @@ -21730,40 +33043,60 @@ namespace cimg_library { //@{ //--------------------------------------- - //! Compute the correlation of the image instance by a mask. + //! Correlate image by a kernel. /** - The correlation of the image instance \p *this by the mask \p mask is defined to be : - - res(x,y,z) = sum_{i,j,k} (*this)(x+i,y+j,z+k)*mask(i,j,k) - - \param mask = the correlation kernel. - \param border_conditions = the border condition type (0=zero, 1=dirichlet) + \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 - CImg& correlate(const CImg& mask, const unsigned int border_conditions=1, const bool is_normalized=false) { - if (is_empty() || !mask) return *this; - return get_correlate(mask,border_conditions,is_normalized).move_to(*this); + CImg& correlate(const CImg& 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 - CImg<_cimg_Ttfloat> get_correlate(const CImg& mask, const unsigned int border_conditions=1, + CImg<_cimg_Ttfloat> get_correlate(const CImg& kernel, const bool boundary_conditions=true, const bool is_normalized=false) const { - if (is_empty() || !mask) return *this; + return _correlate(kernel,boundary_conditions,is_normalized,false); + } + + //! Correlate image by a kernel \newinstance. + template + CImg<_cimg_Ttfloat> _correlate(const CImg& 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 res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum)); - if (border_conditions && mask._width==mask._height && ((mask._depth==1 && mask._width<=5) || (mask._depth==mask._width && mask._width<=3))) { - // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with border_conditions=1) + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + cimg_abort_init; + if (boundary_conditions && kernel._width==kernel._height && + ((kernel._depth==1 && kernel._width<=5) || (kernel._depth==kernel._width && kernel._width<=3))) { + // Special optimization done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 kernel (with boundary_conditions=true). + CImg _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(); + Ttfloat *ptrd = res._data; - switch (mask._depth) { + CImg I; + switch (_kernel._depth) { case 3 : { - T I[27] = { 0 }; + I.assign(27); cimg_forC(res,c) { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg _K = _kernel.get_shared_channel(c%_kernel._spectrum); if (is_normalized) { - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; - cimg_forZ(_img,z) cimg_for3x3x3(_img,x,y,z,0,I,T) { + 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] + @@ -21773,710 +33106,1067 @@ namespace cimg_library { 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]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + - I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + - I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + - I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] + - I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + - I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + - I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + - I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + - I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26])/std::sqrt(N):0); + *(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_forZ(_img,z) cimg_for3x3x3(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + - I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + - I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + - I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] + - I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + - I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + - I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + - I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + - I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26]); + } 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 : { - T I[8] = { 0 }; + I.assign(8); cimg_forC(res,c) { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); if (is_normalized) { - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; - cimg_forZ(_img,z) cimg_for2x2x2(_img,x,y,z,0,I,T) { + 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]*_mask[0] + I[1]*_mask[1] + - I[2]*_mask[2] + I[3]*_mask[3] + - I[4]*_mask[4] + I[5]*_mask[5] + - I[6]*_mask[6] + I[7]*_mask[7])/std::sqrt(N):0); + *(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_forZ(_img,z) cimg_for2x2x2(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + - I[2]*_mask[2] + I[3]*_mask[3] + - I[4]*_mask[4] + I[5]*_mask[5] + - I[6]*_mask[6] + I[7]*_mask[7]); + } 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 (mask._width) { + switch (_kernel._width) { case 6 : { - T I[36] = { 0 }; + I.assign(36); cimg_forC(res,c) { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); if (is_normalized) { - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + 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]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + - I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] + - I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + - I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + - I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] + - I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35])/std::sqrt(N):0); + 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]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + - I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] + - I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + - I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + - I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] + - I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35]); + *(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 : { - T I[25] = { 0 }; + I.assign(25); cimg_forC(res,c) { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); if (is_normalized) { - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + 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]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + - I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + - I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + - I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] + - I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24])/std::sqrt(N):0); + *(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]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + - I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + - I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + - I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] + - I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24]); + *(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 : { - T I[16] = { 0 }; + I.assign(16); cimg_forC(res,c) { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); if (is_normalized) { - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + 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]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + - I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + - I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] + - I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15])/std::sqrt(N):0); + *(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]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + - I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + - I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] + - I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15]); + *(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 : { - T I[9] = { 0 }; + I.assign(9); cimg_forC(res,c) { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); if (is_normalized) { - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + 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]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] + - I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] + - I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8])/std::sqrt(N):0); + *(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]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] + - I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] + - I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8]); + *(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 : { - T I[4] = { 0 }; + I.assign(4); cimg_forC(res,c) { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); if (is_normalized) { - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + 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]*_mask[0] + I[1]*_mask[1] + - I[2]*_mask[2] + I[3]*_mask[3])/std::sqrt(N):0); + *(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]*_mask[0] + I[1]*_mask[1] + - I[2]*_mask[2] + I[3]*_mask[3]); + *(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 _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); - res.get_shared_channel(c).assign(_img)*=_mask[0]; + const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); + res.get_shared_channel(c).assign(_img)*=K[0]; } break; } } - } else { // Generic version for other masks and borders conditions. + } else { // Generic version for other kernels and boundary conditions. + 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 - mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2, - mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; - cimg_forC(res,c) { + cimg_forC(res,c) cimg_abort_try { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); if (is_normalized) { // Normalized correlation. - const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M; + const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=32768)) for (int z = mz1; z=mye || z=mze)?++x:((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*_mask(mx1+xm,my1+ym,mz1+zm); + 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_catch2() + if (boundary_conditions) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((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_catch2() else - cimg_forYZ(res,y,z) - for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((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,0); - val+=_val*_mask(mx1+xm,my1+ym,mz1+zm); + 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_catch2() } else { // Classical correlation. - for (int z = mz1; z=32768)) + for (int z = mz1; z=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + } cimg_abort_catch2() + if (boundary_conditions) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((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)*_mask(mx1+xm,my1+ym,mz1+zm); + 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_catch2() else - cimg_forYZ(res,y,z) - for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((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,0)*_mask(mx1+xm,my1+ym,mz1+zm); + 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_catch2() } - } + } cimg_abort_catch() } + cimg_abort_test(); return res; } - //! Compute the convolution of the image by a mask. + //! Convolve image by a kernel. /** - The result \p res of the convolution of an image \p img by a mask \p mask is defined to be : - - res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k) - - \param mask = the correlation kernel. - \param border_conditions = the border condition type (0=zero, 1=dirichlet) + \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 - CImg& convolve(const CImg& mask, const unsigned int border_conditions=1, const bool is_normalized=false) { - if (is_empty() || !mask) return *this; - return get_convolve(mask,border_conditions,is_normalized).move_to(*this); + CImg& convolve(const CImg& 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 - CImg<_cimg_Ttfloat> get_convolve(const CImg& mask, const unsigned int border_conditions=1, + CImg<_cimg_Ttfloat> get_convolve(const CImg& kernel, const bool boundary_conditions=true, const bool is_normalized=false) const { - if (is_empty() || !mask) return *this; - return get_correlate(CImg(mask._data,mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),border_conditions,is_normalized); + return _correlate(CImg(kernel._data,kernel.size(),1,1,1,true).get_mirror('x'). + resize(kernel,-1),boundary_conditions,is_normalized,true); } - //! Return the erosion of the image by a structuring element. - template - CImg& erode(const CImg& mask, const unsigned int border_conditions=1, const bool is_normalized=false) { - if (is_empty() || !mask) return *this; - return get_erode(mask,border_conditions,is_normalized).move_to(*this); - } - - template - CImg<_cimg_Tt> get_erode(const CImg& mask, const unsigned int border_conditions=1, - const bool is_normalized=false) const { - if (is_empty() || !mask) return *this; - typedef _cimg_Tt Tt; - CImg res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum)); - const int - mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2, - mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), - mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; - cimg_forC(*this,c) { - const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); - if (is_normalized) { // Normalized erosion. - for (int z = mz1; z::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 = _mask(mx1+xm,my1+ym,mz1+zm); - const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) + mval); - if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::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 = _mask(mx1+xm,my1+ym,mz1+zm); - const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) + mval); - if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::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 = _mask(mx1+xm,my1+ym,mz1+zm); - const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) + mval); - if (mval && cval::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const Tt cval = (Tt)_img(x+xm,y+ym,z+zm); - if (_mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm); - if (_mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0); - if (_mask(mx1+xm,my1+ym,mz1+zm) && cval& 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, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; - CImg buf(L); - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + //! 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& cumulate(const char axis=0) { + switch (cimg::lowercase(axis)) { + case 'x' : + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=512 && _height*_depth*_spectrum>=16)) cimg_forYZC(*this,y,z,c) { - const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - ptrd = buf._data; 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; - for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs0; --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 (nval0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _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 buf(L); - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + break; + case 'y' : { + const ulongT w = (ulongT)_width; + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_height>=512 && _width*_depth*_spectrum>=16)) cimg_forXZC(*this,x,z,c) { - const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - ptrd = buf._data; 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; - for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs0; --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 (nval0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _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 buf(L); - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + } break; + case 'z' : { + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_depth>=512 && _width*_depth*_spectrum>=16)) cimg_forXYC(*this,x,y,c) { - const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; - ptrd = buf._data; 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; - for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs0; --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 (nval0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val=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; } - CImg get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { - return (+*this).erode(sx,sy,sz); + //! Cumulate image values, optionally along specified axis \newinstance. + CImg get_cumulate(const char axis=0) const { + return CImg(*this,false).cumulate(axis); } - //! Erode the image by a square structuring element of size sx. - CImg& erode(const unsigned int s) { - return erode(s,s,s); + //! 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& cumulate(const char *const axes) { + for (const char *s = axes; *s; ++s) cumulate(*s); + return *this; } - CImg get_erode(const unsigned int s) const { - return (+*this).erode(s); + //! Cumulate image values, along specified axes \newinstance. + CImg get_cumulate(const char *const axes) const { + return CImg(*this,false).cumulate(axes); } - //! Dilate the image by a structuring element. + //! 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 - CImg& dilate(const CImg& mask, const unsigned int border_conditions=1, const bool is_normalized=false) { - if (is_empty() || !mask) return *this; - return get_dilate(mask,border_conditions,is_normalized).move_to(*this); + CImg& erode(const CImg& 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 - CImg<_cimg_Tt> get_dilate(const CImg& mask, const unsigned int border_conditions=1, - const bool is_normalized=false) const { - if (is_empty() || !mask) return *this; + CImg<_cimg_Tt> get_erode(const CImg& 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(width(),height(),depth(),spectrum(),0); typedef _cimg_Tt Tt; - CImg res(_width,_height,_depth,_spectrum); + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); const int - mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2, - mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), + 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; - cimg_forC(*this,c) { + cimg_abort_init; + cimg_forC(*this,c) cimg_abort_try { + cimg_abort_test(); const CImg _img = get_shared_channel(c%_spectrum); - const CImg _mask = mask.get_shared_channel(c%mask._spectrum); - if (is_normalized) { // Normalized dilation. + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real erosion + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=32768)) for (int z = mz1; z::min(); + for (int x = mx1; x::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 = _mask(mx1+xm,my1+ym,mz1+zm); - const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) - mval); - if (mval && cval>max_val) max_val = cval; + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(_img(x + xm,y + ym,z + zm) - mval); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); + res(x,y,z,c) = min_val; + } cimg_abort_catch2() + if (boundary_conditions) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::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 = _mask(mx1+xm,my1+ym,mz1+zm); - const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) - mval); - if (mval && cval>max_val) max_val = cval; + 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=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::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 = _mask(mx1+xm,my1+ym,mz1+zm); - const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) - mval); - if (mval && cval>max_val) max_val = cval; + 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=32768)) for (int z = mz1; z::min(); + for (int x = mx1; x::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const Tt cval = (Tt)_img(x+xm,y+ym,z+zm); - if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; - } - if (border_conditions) - cimg_forYZ(res,y,z) - for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); + 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=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm); - if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; + 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=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0); - if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; - } - res(x,y,z,c) = max_val; + 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& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + //! 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& 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 buf(L); - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + 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; - ptrd = buf._data; 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; - for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs=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; + 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 (ptrs0; --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 (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val0 && 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(_width-1,y,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; } } } 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; + 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 buf(L); - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + 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; - ptrd = buf._data; 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; - for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs=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; + 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 (ptrs0; --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 (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val0 && 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,_height-1,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; } } } 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; + 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 buf(L); - T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + 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; - ptrd = buf._data; 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; - for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs=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; + 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 (ptrs0; --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 (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val0 && 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,_depth-1,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; } } } return *this; } + //! Erode image by a rectangular structuring element of specified size \newinstance. + CImg 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& erode(const unsigned int s) { + return erode(s,s,s); + } + + //! Erode the image by a square structuring element of specified size \newinstance. + CImg 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 + CImg& dilate(const CImg& 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 + CImg<_cimg_Tt> get_dilate(const CImg& 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 res(_width,_height,_depth,_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; + cimg_abort_init; + cimg_forC(*this,c) cimg_abort_try { + cimg_abort_test(); + const CImg _img = get_shared_channel(c%_spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real dilation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=32768)) + for (int z = mz1; z::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_catch2() + if (boundary_conditions) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::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_catch2() + else + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(*this,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::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_catch2() + } else { // Binary dilation + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + for (int z = mz1; z::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_catch2() + if (boundary_conditions) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::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_catch2() + else + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_forYZ(res,y,z) cimg_abort_try2 { + cimg_abort_test2(); + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::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_catch2() + } + } cimg_abort_catch() + 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& 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 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=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 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=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 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=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 get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { return (+*this).dilate(sx,sy,sz); } - //! Erode the image by a square structuring element of size sx. + //! Dilate image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ CImg& dilate(const unsigned int s) { return dilate(s,s,s); } + //! Dilate image by a square structuring element of specified size \newinstance. CImg get_dilate(const unsigned int s) const { return (+*this).dilate(s); } - //! Compute the watershed transform, from an image instance of non-zero labels. + //! 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 - CImg& watershed(const CImg& priority, const bool fill_lines=true) { + CImg& watershed(const CImg& 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 in_queue(_width,_height,_depth,1,0); + CImg labels(_width,_height,_depth,1,0), seeds(64,3); CImg::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++)) { - if (x-1>=0 && !(*this)(x-1,y,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z); - if (x+1=0 && !(*this)(x,y-1,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z); - if (y+1=0 && !(*this)(x,y,z-1)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1); - if (z+1=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=0; is_ny = ny=0; is_nz = nz=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz=0) { - if ((*this)(x-1,y,z)) { if (!label) label = (*this)(x-1,y,z); else if (label!=(*this)(x-1,y,z)) is_same_label = false; } - else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z); - } - if (x+1=0) { - if ((*this)(x,y-1,z)) { if (!label) label = (*this)(x,y-1,z); else if (label!=(*this)(x,y-1,z)) is_same_label = false; } - else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z); - } - if (y+1=0) { - if ((*this)(x,y,z-1)) { if (!label) label = (*this)(x,y,z-1); else if (label!=(*this)(x,y,z-1)) is_same_label = false; } - else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1); - } - if (z+1=0 && (*this)(x-1,y,z)) || (x+1=0 && (*this)(x,y-1,z)) || (y+1=0 && (*this)(x,y,z-1)) || (z+1>depth() && (*this)(x,y,z+1)))) - Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z),x,y,z); - - // Start line filling process. - while (sizeQ) { - const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); - Q._priority_queue_remove(sizeQ); - t pmax = cimg::type::min(); - int xmax = 0, ymax = 0, zmax = 0; - if (x-1>=0) { - if ((*this)(x-1,y,z)) { if (priority(x-1,y,z)>pmax) { pmax = priority(x-1,y,z); xmax = x-1; ymax = y; zmax = z; }} - else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z); - } - if (x+1pmax) { pmax = priority(x+1,y,z); xmax = x+1; ymax = y; zmax = z; }} - else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z); - } - if (y-1>=0) { - if ((*this)(x,y-1,z)) { if (priority(x,y-1,z)>pmax) { pmax = priority(x,y-1,z); xmax = x; ymax = y-1; zmax = z; }} - else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z); - } - if (y+1pmax) { pmax = priority(x,y+1,z); xmax = x; ymax = y+1; zmax = z; }} - else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z); - } - if (z-1>=0) { - if ((*this)(x,y,z-1)) { if (priority(x,y,z-1)>pmax) { pmax = priority(x,y,z-1); xmax = x; ymax = y; zmax = z-1; }} - else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1); - } - if (z+1pmax) { pmax = priority(x,y,z+1); xmax = x; ymax = y; zmax = z+1; }} - else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1); - } - (*this)(x,y,z) = (*this)(xmax,ymax,zmax); + unsigned int xs, ys, zs, ns, nmin = 0; + float d, dmin = cimg::type::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 - CImg get_watershed(const CImg& priority, const bool fill_lines=true) const { - return (+*this).watershed(priority,fill_lines); + CImg get_watershed(const CImg& priority, const bool is_high_connectivity=false) const { + return (+*this).watershed(priority,is_high_connectivity); } - // Insert/Remove items in priority queue, for watershed/distance transforms. - template - bool _priority_queue_insert(CImg& in_queue, unsigned int& siz, const t value, const unsigned int x, const unsigned int y, const unsigned int z) { - if (in_queue(x,y,z)) return false; - in_queue(x,y,z) = true; + // [internal] Insert/Remove items in priority queue, for watershed/distance transforms. + template + bool _priority_queue_insert(CImg& 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)); + (*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& _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); + (*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))(*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)); + 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)); + 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)); + 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; } - //! Compute the result of the Deriche filter. + //! Apply recursive Deriche filter. /** - The Canny-Deriche filter is a recursive algorithm allowing to compute blurred derivatives of - order 0,1 or 2 of an image. + \param sigma Standard deviation of the filter. + \param order Order of the filter. Can be { 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. **/ - CImg& deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) { -#define _cimg_deriche2_apply \ + CImg& deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) { +#define _cimg_deriche_apply \ + CImg Y(N); \ Tfloat *ptrY = Y._data, yb = 0, yp = 0; \ T xp = (T)0; \ - if (cond) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \ + if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \ for (int m = 0; m=0; --n) { \ + 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::uncase(axis); + 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.1 && !order)) return *this; + if (is_empty() || (nsigma<0.1f && !order)) return *this; const float nnsigma = nsigma<0.1f?0.1f:nsigma, alpha = 1.695f/nnsigma, @@ -22644,161 +34328,419 @@ namespace cimg_library { 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); + const float k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2); a0 = k; - a1 = k*(alpha-1)*ema; - a2 = k*(alpha+1)*ema; + a1 = k*(alpha - 1)*ema; + a2 = k*(alpha + 1)*ema; a3 = -k*ema2; } break; case 1 : { - const float k = (1-ema)*(1-ema)/ema; - a0 = k*ema; - a1 = a3 = 0; - a2 = -a0; + 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)); + 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; + 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 " + "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); + coefp = (a0 + a1)/(1 + b1 + b2); + coefn = (a2 + a3)/(1 + b1 + b2); switch (naxis) { case 'x' : { - const int N = _width, off = 1; - CImg Y(N); - cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche2_apply; } + const int N = width(); + const ulongT off = 1U; + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=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, off = _width; - CImg Y(N); - cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche2_apply; } + const int N = height(); + const ulongT off = (ulongT)_width; + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=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, off = _width*_height; - CImg Y(N); - cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche2_apply; } + const int N = depth(); + const ulongT off = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=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, off = _width*_height*_depth; - CImg Y(N); - cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche2_apply; } + const int N = spectrum(); + const ulongT off = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; } } } return *this; } - CImg get_deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) const { - return CImg(*this,false).deriche(sigma,order,axis,cond); + //! Apply recursive Deriche filter \newinstance. + CImg get_deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).deriche(sigma,order,axis,boundary_conditions); } - //! Return a blurred version of the image, using a Canny-Deriche filter. + // [internal] Apply a recursive filter (used by CImg::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 { 0=dirichlet | 1=neumann }. + \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.0 / ( (1.0 + a1 - a2 + a3) * (1.0 - a1 - a2 - a3) * (1.0 + a2 + (a1 - a3) * a3) ); + double M[9]; // Triggs matrix + M[0] = scaleM * (-a3 * a1 + 1.0 - 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.0) * (a2 + a3 * a1); + M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.0); + 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.0 - a1 - a2 - a3), vplus = uplus/(1.0 - 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; n0; --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; n0; --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; n0; --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; n0; --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. /** - Blur the image with an anisotropic exponential filter (Deriche filter of order 0). + \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 { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \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& blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) { - if (!is_empty()) { - if (_width>1) deriche(sigmax,0,'x',cond); - if (_height>1) deriche(sigmay,0,'y',cond); - if (_depth>1) deriche(sigmaz,0,'z',cond); + CImg& vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) { + if (is_empty()) return *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>=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>=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>=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>=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; } - CImg get_blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) const { - return CImg(*this,false).blur(sigmax,sigmay,sigmaz,cond); + //! Blur image using Van Vliet recursive Gaussian filter. \newinstance. + CImg get_vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions); } - //! Return a blurred version of the image, using a Canny-Deriche filter. - CImg& blur(const float sigma, const bool cond=true) { - const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; - return blur(nsigma,nsigma,nsigma,cond); - } - - CImg get_blur(const float sigma, const bool cond=true) const { - return CImg(*this,false).blur(sigma,cond); - } - - //! Blur the image anisotropically following a field of diffusion tensors. + //! Blur image. /** - \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 gaussian function. - \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) - \param fast_approx = Tell to use the fast approximation or not. + \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 { false=dirichlet | true=neumann }. + \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& 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 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(*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 { 0=dirichlet | 1=neumann }.a + \param is_gaussian Use a gaussian kernel (VanVliet) is set, a pseudo-gaussian (Deriche) otherwise. + \see deriche(), vanvliet(). + **/ + CImg& 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 get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const { + return CImg(*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 { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. **/ template CImg& blur_anisotropic(const CImg& 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 fast_approx=1) { + 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).", + "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() || amplitude<=0 || dl<0) return *this; const bool is_3d = (G._spectrum==6); T val_min, val_max = max_min(val_min); + cimg_abort_init; if (da<=0) { // Iterated oriented Laplacians CImg velocity(_width,_height,_depth,_spectrum); for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) { Tfloat *ptrd = velocity._data, veloc_max = 0; - if (is_3d) { // 3d version - CImg_3x3x3(I,Tfloat); - cimg_forC(*this,c) 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; + 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_3x3(I,Tfloat); - cimg_forZC(*this,z,c) 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; + 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 unsigned int whd = _width*_height*_depth; + const ulongT whd = (ulongT)_width*_height*_depth; const float sqrt2amplitude = (float)std::sqrt(2*amplitude); const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1; - CImg res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum); + CImg 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 = (180%(int)da)/2.0f; phi<=180; phi+=da) { - const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), da2 = datmp<1?360.0f:datmp; + for (float phi = cimg::mod(180.0f,da)/2.0f; phi<=180; phi+=da) { + const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), + da2 = datmp<1?360.0f:datmp; for (float theta = 0; theta<360; (theta+=da2),++N) { const float thetar = (float)(theta*cimg::PI/180), @@ -22815,7 +34757,7 @@ namespace cimg_library { 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 = (float)std::sqrt(1e-5+u*u+v*v+w*w), + n = 1e-5f + cimg::hypot(u,v,w), dln = dl/n; *(pd0++) = (Tfloat)(u*dln); *(pd1++) = (Tfloat)(v*dln); @@ -22823,82 +34765,88 @@ namespace cimg_library { *(pd3++) = (Tfloat)n; } - Tfloat *ptrd = res._data; - cimg_forXYZ(*this,x,y,z) { - 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=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 (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; + cimg_abort_test(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=2) + firstprivate(val)) + cimg_forYZ(*this,y,z) cimg_abort_try2 { + 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=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; } - X+=u; Y+=v; Z+=w; - } - } break; - case 1 : { // Linear interpolation - for (float l = 0; l=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 (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; + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=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; } - X+=u; Y+=v; Z+=w; - } - } break; - default : { // 2nd order Runge Kutta - for (float l = 0; l=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 (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; + } break; + default : { // 2nd order Runge Kutta + for (float l = 0; l=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; } - X+=u; Y+=v; Z+=w; + } break; } - } 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; } } - Tfloat *_ptrd = ptrd++; - 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_catch2() } } } else { // 2d LIC algorithm - for (float theta = (360%(int)da)/2.0f; theta<360; (theta+=da),++N) { - const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar)); + for (float theta = cimg::mod(360.0f,da)/2.0f; 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) { @@ -22906,192 +34854,250 @@ namespace cimg_library { const float u = (float)(a*vx + b*vy), v = (float)(b*vx + c*vy), - n = (float)std::sqrt(1e-5+u*u+v*v), + n = std::max(1e-5f,cimg::hypot(u,v)), dln = dl/n; *(pd0++) = (Tfloat)(u*dln); *(pd1++) = (Tfloat)(v*dln); *(pd2++) = (Tfloat)n; } - Tfloat *ptrd = res._data; - cimg_forXY(*this,x,y) { - 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=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 (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; + + cimg_abort_test(); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=2) firstprivate(val)) + cimg_forY(*this,y) cimg_abort_try2 { + 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=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; } - X+=u; Y+=v; - } - } break; - case 1 : { // Linear interpolation - for (float l = 0; l=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 (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; + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=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; } - X+=u; Y+=v; - } - } break; - default : { // 2nd-order Runge-kutta interpolation - for (float l = 0; l=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 (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; + } break; + default : { // 2nd-order Runge-kutta interpolation + for (float l = 0; l=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; } - 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; } } - } - Tfloat *_ptrd = ptrd++; - 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_catch2() } } - const Tfloat *ptrs = res.end(); - cimg_for(*this,ptrd,T) { const Tfloat val = *(--ptrs)/N; *ptrd = valval_max?val_max:(T)val); } + const Tfloat *ptrs = res._data; + cimg_for(*this,ptrd,T) { + const Tfloat val = *(ptrs++)/N; + *ptrd = valval_max?val_max:(T)val); + } } + cimg_abort_test(); return *this; } + //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance. template - CImg get_blur_anisotropic(const CImg& 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 fast_approx=true) const { - return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); + CImg get_blur_anisotropic(const CImg& 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(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); } - //! Blur an image following in an anisotropic way. + //! 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 { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ CImg& 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 fast_approx=true) { - return blur_anisotropic(get_edge_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3), - amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); + const bool is_fast_approx=true) { + return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3), + amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); } - CImg 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 fast_approx=true) const { - return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx); + //! Blur image anisotropically, in an edge-preserving way \newinstance. + CImg 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(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec, + interpolation_type,is_fast_approx); } - //! Blur an image using the bilateral filter. + //! 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 range axis. - \param bgrid_x Size of the bilateral grid along the X-axis. - \param bgrid_y Size of the bilateral grid along the Y-axis. - \param bgrid_z Size of the bilateral grid along the Z-axis. - \param bgrid_r Size of the bilateral grid along the range axis. - \param interpolation_type Use interpolation for image slicing. + \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 **/ - CImg& blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, - const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, - const bool interpolation_type=true) { - if (is_empty()) return *this; - T m, M = max_min(m); - const float range = (float)(1.0f + M - m); - const unsigned int - bx0 = bgrid_x>=0?bgrid_x:_width*(-bgrid_x)/100, - by0 = bgrid_y>=0?bgrid_y:_height*(-bgrid_y)/100, - bz0 = bgrid_z>=0?bgrid_z:_depth*(-bgrid_z)/100, - br0 = bgrid_r>=0?bgrid_r:(int)(-range*bgrid_r/100), - bx = bx0>0?bx0:1, - by = by0>0?by0:1, - bz = bz0>0?bz0:1, - br = br0>0?br0:1; + template + CImg& blur_bilateral(const CImg& 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, - nsigma_x = _sigma_x*bx/_width, - nsigma_y = _sigma_y*by/_height, - nsigma_z = _sigma_z*bz/_depth, - nsigma_r = sigma_r*br/range; - if (nsigma_x>0 || nsigma_y>0 || nsigma_z>0 || nsigma_r>0) { + _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.0f), + _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.0f), + _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.0f), + _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 bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); cimg_forC(*this,c) { + const CImg _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 int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range); - bgrid(X,Y,Z,R) += (float)val; - bgridw(X,Y,Z,R) += 1; + 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(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false); - bgridw.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false); - if (interpolation_type) cimg_forXYZ(*this,x,y,z) { - const T val = (*this)(x,y,z,c); - const float X = (float)x*bx/_width, Y = (float)y*by/_height, Z = (float)z*bz/_depth, R = (float)((val-m)*br/range), - 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 cimg_forXYZ(*this,x,y,z) { - const T val = (*this)(x,y,z,c); - const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range); - const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R); + 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()>=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 bgrid(bx,by,br,2); cimg_forC(*this,c) { + const CImg _guide = guide.get_shared_channel(c%guide._spectrum); bgrid.fill(0); cimg_forXY(*this,x,y) { const T val = (*this)(x,y,c); - const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range); - bgrid(X,Y,R,0) += (float)val; - bgrid(X,Y,R,1) += 1; + 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(nsigma_x,nsigma_y,0,true).blur(0,0,nsigma_r,false); - if (interpolation_type) cimg_forXY(*this,x,y) { - const T val = (*this)(x,y,c); - const float X = (float)x*bx/_width, Y = (float)y*by/_height, R = (float)((val-m)*br/range), - bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1); - (*this)(x,y,c) = (T)(bval0/bval1); - } else cimg_forXY(*this,x,y) { - const T val = (*this)(x,y,c); - const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range); - const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,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()>=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); } } @@ -23100,43 +35106,310 @@ namespace cimg_library { return *this; } - CImg get_blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r, - const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r, - const bool interpolation_type=true) const { - return (+*this).blur_bilateral(sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,interpolation_type); + //! Blur image, with the joint bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& 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(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r, + sampling_x,sampling_y,sampling_z,sampling_r); } - //! Blur an image using the bilateral filter. - CImg& blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, - const bool interpolation_type=true) { - const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; - return blur_bilateral(nsigma_s,nsigma_s,nsigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type); + //! 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 + CImg& blur_bilateral(const CImg& 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); } - CImg get_blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32, - const bool interpolation_type=true) const { - return (+*this).blur_bilateral(sigma_s,sigma_s,sigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type); + //! Blur image using the bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& guide, + const float sigma_s, const float sigma_r, + const float sampling_s=0, const float sampling_r=0) const { + return CImg(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r); } - //! Blur an image in its patch-based space. + // [internal] Apply a box filter (used by CImg::boxfilter() and CImg::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 { 0=dirichlet | 1=neumann }. + */ + 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 win(winsize); + for (unsigned int iter = 0; iter=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 { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param nb_iter Number of filter iterations. + **/ + CImg& 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>=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>=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>=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>=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 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(*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 { false=dirichlet | true=neumann }. + \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& 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 get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, + const bool boundary_conditions=true) const { + return CImg(*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 { 0=dirichlet | 1=neumann }.a + \see deriche(), vanvliet(). + **/ + CImg& 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 get_blur_box(const float boxsize, const bool boundary_conditions=true) const { + return CImg(*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 + CImg& blur_guided(const CImg& 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 + CImg get_blur_guided(const CImg& 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); + const CImg N = CImg(_width,_height,_depth,1,1)._blur_guided(psize); + CImg + mean_I = CImg(guide,false)._blur_guided(psize).div(N), + mean_p = CImg(*this,false)._blur_guided(psize).div(N), + cov_Ip = CImg(*this,false).mul(guide)._blur_guided(psize).div(N)-=mean_p.get_mul(mean_I), + var_I = CImg(guide,false).sqr()._blur_guided(psize).div(N)-=mean_I.get_sqr(), + &a = cov_Ip.div(var_I+=_regularization), + &b = mean_p-=a.get_mul(mean_I); + a._blur_guided(psize).div(N); + b._blur_guided(psize).div(N); + return a.mul(guide)+=b; + } + + // [internal] Perform box filter with dirichlet boundary conditions. + CImg& _blur_guided(const unsigned int psize) { + const int p1 = (int)psize/2, p2 = (int)psize - p1; + if (_depth!=1) { + CImg cumul = get_cumulate('z'), cumul2 = cumul.get_shift(0,0,p2,0,1); + (cumul.shift(0,0,-p1,0,1)-=cumul2).move_to(*this); + } + if (_height!=1) { + CImg cumul = get_cumulate('y'), cumul2 = cumul.get_shift(0,p2,0,0,1); + (cumul.shift(0,-p1,0,0,1)-=cumul2).move_to(*this); + } + if (_width!=1) { + CImg cumul = get_cumulate('x'), cumul2 = cumul.get_shift(p2,0,0,0,1); + (cumul.shift(-p1,0,0,0,1)-=cumul2).move_to(*this); + } + return *this; + } + + //! 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 patchs. + \param lookup_size Size of the window to search similar patchs. + \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& 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 fast_approx=true) { + 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,fast_approx).move_to(*this); + return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this); } - CImg 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 fast_approx=true) const { + //! Blur image using patch-based space \newinstance. + CImg 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; \ + 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(img(x,y,z,0) - img(p,q,r,0))3?0.0f:1.0f; \ @@ -23150,12 +35423,13 @@ namespace cimg_library { #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; \ + 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.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \ + 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); \ @@ -23176,7 +35450,7 @@ namespace cimg_library { cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0,0) - img(p,q,0,0))3?0.0f:1.0f; \ @@ -23195,7 +35469,7 @@ namespace cimg_library { 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.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \ + 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); \ @@ -23208,7 +35482,7 @@ namespace cimg_library { else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ } - if (is_empty() || !patch_size || !lookup_size) return (+*this); + if (is_empty() || !patch_size || !lookup_size) return +*this; CImg res(_width,_height,_depth,_spectrum,0); const CImg _img = smoothness>0?get_blur(smoothness):CImg(),&img = smoothness>0?_img:*this; CImg P(patch_size*patch_size*_spectrum), Q(P); @@ -23218,316 +35492,425 @@ namespace cimg_library { 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 (fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break; - case 3 : if (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 (fast_approx) 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(img(x,y,z,0)-img(p,q,r,0))3?0.0f:1.0f; - 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_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)); + 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>=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(img(x,y,z,0)-img(p,q,r,0))3?0.0f:1.0f; + 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>=32 && res._height>=4) firstprivate(P,Q)) + cimg_forXY(res,x,y) { // 2d fast approximation. + 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 (cimg::abs(img(x,y,0)-img(p,q,0))3?0.0f:1.0f; + 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>=32 && res._height>=4) firstprivate(P,Q)) + cimg_forXY(res,x,y) { // 2d exact algorithm. + 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)); + } } - } - } else switch (patch_size) { // 2d - case 2 : if (fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break; - case 3 : if (fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break; - case 4 : if (fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break; - case 5 : if (fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break; - case 6 : if (fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break; - case 7 : if (fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break; - case 8 : if (fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break; - case 9 : if (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 (fast_approx) cimg_forXY(res,x,y) { // 2d fast approximation. - 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 (cimg::abs(img(x,y,0)-img(p,q,0))3?0.0f:1.0f; - 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_forXY(res,x,y) { // 2d exact algorithm. - 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; } - //! Apply a median filter. - CImg& blur_median(const unsigned int n) { + //! 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& blur_median(const unsigned int n, const float threshold=0) { if (!n) return *this; - return get_blur_median(n).move_to(*this); + return get_blur_median(n,threshold).move_to(*this); } - CImg get_blur_median(const unsigned int n) const { - if (is_empty() || n<=1) return (+*this); + //! Blur image with the median filter \newinstance. + CImg get_blur_median(const unsigned int n, const float threshold=0) const { + if (is_empty() || n<=1) return +*this; CImg res(_width,_height,_depth,_spectrum); T *ptrd = res._data; - const int hl = n/2, hr = hl - 1 + n%2; - if (res._depth!=1) cimg_forXYZC(*this,x,y,z,c) { // 3d - 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; - *(ptrd++) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median(); + 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>=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 float val0 = (float)(*this)(x,y,z,c); + CImg 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((float)(*this)(p,q,r,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; } + res(x,y,z,c) = values.get_shared_points(0,nb_values - 1).median(); + } + else + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=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 { #define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b) - if (res._height!=1) switch (n) { // 2d - case 3 : { - T I[9] = { 0 }; - CImg_3x3(J,T); - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { - std::memcpy(J,I,9*sizeof(T)); - _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn); - _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn); - _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn); - _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn); - _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc); - _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc); - _cimg_median_sort(Jcc, Jnp); - *(ptrd++) = Jcc; - } - } break; - case 5 : { - T I[25] = { 0 }; - CImg_5x5(J,T); - cimg_forC(*this,c) cimg_for5x5(*this,x,y,0,c,I,T) { - std::memcpy(J,I,25*sizeof(T)); - _cimg_median_sort(Jbb, Jpb); _cimg_median_sort(Jnb, Jab); _cimg_median_sort(Jcb, Jab); _cimg_median_sort(Jcb, Jnb); - _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbp, Jcp); _cimg_median_sort(Jbp, Jpp); _cimg_median_sort(Jap, Jbc); - _cimg_median_sort(Jnp, Jbc); _cimg_median_sort(Jnp, Jap); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jpc, Jnc); - _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jbn, Jpn); _cimg_median_sort(Jac, Jpn); _cimg_median_sort(Jac, Jbn); - _cimg_median_sort(Jnn, Jan); _cimg_median_sort(Jcn, Jan); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpa, Jca); - _cimg_median_sort(Jba, Jca); _cimg_median_sort(Jba, Jpa); _cimg_median_sort(Jna, Jaa); _cimg_median_sort(Jcb, Jbp); - _cimg_median_sort(Jnb, Jpp); _cimg_median_sort(Jbb, Jpp); _cimg_median_sort(Jbb, Jnb); _cimg_median_sort(Jab, Jcp); - _cimg_median_sort(Jpb, Jcp); _cimg_median_sort(Jpb, Jab); _cimg_median_sort(Jpc, Jac); _cimg_median_sort(Jnp, Jac); - _cimg_median_sort(Jnp, Jpc); _cimg_median_sort(Jcc, Jbn); _cimg_median_sort(Jap, Jbn); _cimg_median_sort(Jap, Jcc); - _cimg_median_sort(Jnc, Jpn); _cimg_median_sort(Jbc, Jpn); _cimg_median_sort(Jbc, Jnc); _cimg_median_sort(Jba, Jna); - _cimg_median_sort(Jcn, Jna); _cimg_median_sort(Jcn, Jba); _cimg_median_sort(Jpa, Jaa); _cimg_median_sort(Jnn, Jaa); - _cimg_median_sort(Jnn, Jpa); _cimg_median_sort(Jan, Jca); _cimg_median_sort(Jnp, Jcn); _cimg_median_sort(Jap, Jnn); - _cimg_median_sort(Jbb, Jnn); _cimg_median_sort(Jbb, Jap); _cimg_median_sort(Jbc, Jan); _cimg_median_sort(Jpb, Jan); - _cimg_median_sort(Jpb, Jbc); _cimg_median_sort(Jpc, Jba); _cimg_median_sort(Jcb, Jba); _cimg_median_sort(Jcb, Jpc); - _cimg_median_sort(Jcc, Jpa); _cimg_median_sort(Jnb, Jpa); _cimg_median_sort(Jnb, Jcc); _cimg_median_sort(Jnc, Jca); - _cimg_median_sort(Jab, Jca); _cimg_median_sort(Jab, Jnc); _cimg_median_sort(Jac, Jna); _cimg_median_sort(Jbp, Jna); - _cimg_median_sort(Jbp, Jac); _cimg_median_sort(Jbn, Jaa); _cimg_median_sort(Jpp, Jaa); _cimg_median_sort(Jpp, Jbn); - _cimg_median_sort(Jcp, Jpn); _cimg_median_sort(Jcp, Jan); _cimg_median_sort(Jnc, Jpa); _cimg_median_sort(Jbn, Jna); - _cimg_median_sort(Jcp, Jnc); _cimg_median_sort(Jcp, Jbn); _cimg_median_sort(Jpb, Jap); _cimg_median_sort(Jnb, Jpc); - _cimg_median_sort(Jbp, Jcn); _cimg_median_sort(Jpc, Jcn); _cimg_median_sort(Jap, Jcn); _cimg_median_sort(Jab, Jbc); - _cimg_median_sort(Jpp, Jcc); _cimg_median_sort(Jcp, Jac); _cimg_median_sort(Jab, Jpp); _cimg_median_sort(Jab, Jcp); - _cimg_median_sort(Jcc, Jac); _cimg_median_sort(Jbc, Jac); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbc, Jcc); - _cimg_median_sort(Jpp, Jbc); _cimg_median_sort(Jpp, Jcn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcp, Jcn); - _cimg_median_sort(Jcp, Jbc); _cimg_median_sort(Jcc, Jnn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jbc, Jnn); - _cimg_median_sort(Jcc, Jba); _cimg_median_sort(Jbc, Jba); _cimg_median_sort(Jbc, Jcc); - *(ptrd++) = Jcc; - } - } break; - default : { - 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; - *(ptrd++) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); - } - } - } else switch (n) { // 1d - case 2 : { - T I[4] = { 0 }; - cimg_forC(*this,c) cimg_for2x2(*this,x,y,0,c,I,T) *(ptrd++) = (T)(0.5f*(I[0]+I[1])); - } break; - case 3 : { - T I[9] = { 0 }; - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) - *(ptrd++) = I[3]=width()?width()-1:x1; - *(ptrd++) = get_crop(nx0,0,0,c,nx1,0,0,c).median(); - } - } + if (res._height!=1) { // 2d + if (threshold>0) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=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 float val0 = (float)(*this)(x,y,c); + CImg 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((float)(*this)(p,q,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; } + res(x,y,c) = values.get_shared_points(0,nb_values - 1).median(); + } + else switch (n) { // Without threshold. + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + T I[9] = { (T)0 }; + cimg_for3x3(*this,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]); + } + } break; + case 5 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + T I[25] = { (T)0 }; + cimg_for5x5(*this,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]); + } + } break; + case 7 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + T I[49] = { (T)0 }; + cimg_for7x7(*this,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]); + } + } break; + default : { + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=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(); + } + } + } + } else { // 1d + + CImg I; + if (threshold>0) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=16 && _spectrum>=2)) + cimg_forXC(*this,x,c) { // With threshold. + const int + x0 = x - hl, x1 = x + hr, + nx0 = x0<0?0:x0, nx1 = x1>=width()?width() - 1:x1; + const float val0 = (float)(*this)(x,c); + CImg values(n); + unsigned int nb_values = 0; + T *ptrd = values.data(); + cimg_for_inX(*this,nx0,nx1,p) + if (cimg::abs((float)(*this)(p,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,c); ++nb_values; } + res(x,c) = values.get_shared_points(0,nb_values - 1).median(); + } + else switch (n) { // Without threshold. + case 2 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + I.assign(4); + cimg_for2x2(*this,x,y,0,c,I,T) res(x,c) = (T)(0.5f*(I[0] + I[1])); + } + } break; + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + I.assign(9); + cimg_for3x3(*this,x,y,0,c,I,T) + res(x,c) = I[3]=16 && _spectrum>=2)) + cimg_forXC(*this,x,c) { + const int + x0 = x - hl, x1 = x + hr, + nx0 = x0<0?0:x0, nx1 = x1>=width()?width() - 1:x1; + res(x,c) = get_crop(nx0,0,0,c,nx1,0,0,c).median(); + } + } + } } } return res; } - //! Sharpen image using anisotropic shock filters or inverse diffusion. - CImg& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) { + //! Sharpen image. + /** + \param amplitude Sharpening amplitude + \param sharpen_type Select sharpening method. Can be { false=inverse diffusion | true=shock filters }. + \param edge Edge threshold (shock filters only). + \param alpha Gradient smoothness (shock filters only). + \param sigma Tensor smoothness (shock filters only). + **/ + CImg& 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 val, vec, velocity(_width,_height,_depth,_spectrum); - Tfloat *ptrd = velocity._data, veloc_max = 0; + CImg velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum); if (_depth>1) { // 3d - CImg_3x3x3(I,Tfloat); if (sharpen_type) { // Shock filters. CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); if (sigma>0) G.blur(sigma); - Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2), *ptrG3 = G.data(0,0,0,3); - cimg_forXYZ(G,x,y,z) { - 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 collapse(2) cimg_openmp_if(_width>=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 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_forC(*this,c) 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; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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 cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { // Inverse diffusion. - 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; - } - } else { - CImg_3x3(I,Tfloat); + } 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 G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); if (sigma>0) G.blur(sigma); - Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2); - cimg_forXY(G,x,y) { - 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>=32 && _height>=16)) + cimg_forY(G,y) { + CImg 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_forC(*this,c) 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; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=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; } - } else cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { // Inverse diffusion. - 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; - } } + 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); } - CImg get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) const { + //! Sharpen image \newinstance. + CImg 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); } - //! Compute the list of images, corresponding to the XY-gradients of an image. + //! Return image gradient. /** - \param scheme = Numerical scheme used for the gradient computation : + \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 masks - - 3 = Using rotation invariant masks + - 2 = Using Sobel kernels + - 3 = Using rotation invariant kernels - 4 = Using Deriche recusrsive filter. + - 5 = Using Van Vliet recusrsive filter. **/ CImgList get_gradient(const char *const axes=0, const int scheme=3) const { CImgList grad(2,_width,_height,_depth,_spectrum); - Tfloat *ptrd0 = grad[0]._data, *ptrd1 = grad[1]._data; bool is_3d = false; if (axes) { for (unsigned int a = 0; axes[a]; ++a) { - const char axis = cimg::uncase(axes[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'.", + "get_gradient(): Invalid specified axis '%c'.", cimg_instance, axis); } @@ -23535,85 +35918,132 @@ namespace cimg_library { } else is_3d = (_depth>1); if (is_3d) { CImg(_width,_height,_depth,_spectrum).move_to(grad); - Tfloat *ptrd2 = grad[2]._data; switch (scheme) { // 3d. case -1 : { // Backward finite differences. - CImg_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Iccc - Ipcc; - *(ptrd1++) = Iccc - Icpc; - *(ptrd2++) = Iccc - Iccp; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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_2x2x2(I,Tfloat); - cimg_forC(*this,c) cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Incc - Iccc; - *(ptrd1++) = Icnc - Iccc; - *(ptrd2++) = Iccn - Iccc; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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 : { // Using Deriche filter with low standard variation. + 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_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = (Incc - Ipcc)/2; - *(ptrd1++) = (Icnc - Icpc)/2; - *(ptrd2++) = (Iccn - Iccp)/2; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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_3x3(I,Tfloat); - cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Icc - Ipc; - *(ptrd1++) = Icc - Icp; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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_2x2(I,Tfloat); - cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = Inc - Icc; - *(ptrd1++) = Icn - Icc; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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_3x3(I,Tfloat); - const Tfloat a = 1, b = 2; - cimg_forZC(*this,z,c) 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; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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 mask. - CImg_3x3(I,Tfloat); - const Tfloat a = (Tfloat)(0.25f*(2-std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f)-1)); - cimg_forZC(*this,z,c) 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; + case 3 : { // Rotation invariant kernel. + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f) - 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 : { // using Deriche filter with low standard variation + case 4 : { // Van Vliet filter with low standard variation grad[0] = get_deriche(0,1,'x'); grad[1] = get_deriche(0,1,'y'); } break; - default : { // central finite differences - CImg_3x3(I,Tfloat); - cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) { - *(ptrd0++) = (Inc - Ipc)/2; - *(ptrd1++) = (Icn - Icp)/2; + 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>=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 res; for (unsigned int l = 0; axes[l]; ++l) { - const char axis = cimg::uncase(axes[l]); + const char axis = cimg::lowercase(axes[l]); switch (axis) { case 'x' : res.insert(grad[0]); break; case 'y' : res.insert(grad[1]); break; @@ -23624,121 +36054,175 @@ namespace cimg_library { return res; } - //! Get components of the Hessian matrix of an image. + //! Return image hessian. + /** + \param axes Axes considered for the hessian computation, as a C-string (e.g "xy"). + **/ CImgList get_hessian(const char *const axes=0) const { CImgList 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 = std::strlen(naxes); + const unsigned int lmax = (unsigned int)std::strlen(naxes); if (lmax%2) throw CImgArgumentException(_cimg_instance - "get_hessian() : Invalid specified axes '%s'.", + "get_hessian(): Invalid specified axes '%s'.", cimg_instance, naxes); res.assign(lmax/2,_width,_height,_depth,_spectrum); if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d - Tfloat - *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data, - *ptrd3 = res[3]._data, *ptrd4 = res[4]._data, *ptrd5 = res[5]._data; - CImg_3x3x3(I,Tfloat); - cimg_forC(*this,c) 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 + + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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 - Tfloat *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data; - CImg_3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { - *(ptrd0++) = Ipc + Inc - 2*Icc; // Ixx - *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy - *(ptrd2++) = Icp + Icn - 2*Icc; // Iyy + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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; laxis2) cimg::swap(axis1,axis2); bool valid_axis = false; - Tfloat *ptrd = res[l2]._data; if (axis1=='x' && axis2=='x') { // Ixx - valid_axis = true; CImg_3x3(I,Tfloat); - cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc; + valid_axis = true; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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_3x3(I,Tfloat); - cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4; + valid_axis = true; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4; + valid_axis = true; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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_3x3(I,Tfloat); - cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc; + valid_axis = true; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=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_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4; + valid_axis = true; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res[l2].data(0,0,0,c); + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) 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_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc; + valid_axis = true; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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'.", + "get_hessian(): Invalid specified axes '%s'.", cimg_instance, naxes); } return res; } - //! Compute the laplacian of the image instance. + //! Compute image laplacian. CImg& laplacian() { return get_laplacian().move_to(*this); } + //! Compute image laplacian \newinstance. CImg get_laplacian() const { if (is_empty()) return CImg(); CImg res(_width,_height,_depth,_spectrum); - Tfloat *ptrd = res._data; if (_depth>1) { // 3d - CImg_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) - *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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_3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) - *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=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_3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) - *(ptrd++) = Inc + Ipc - 2*Icc; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=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. - CImg& structure_tensors(const unsigned int scheme=2) { - return get_structure_tensors(scheme).move_to(*this); + /** + \param is_fwbw_scheme scheme. Can be { false=centered | true=forward-backward } + **/ + CImg& structure_tensors(const bool is_fwbw_scheme=false) { + return get_structure_tensors(is_fwbw_scheme).move_to(*this); } - CImg get_structure_tensors(const unsigned int scheme=2) const { + //! Compute the structure tensor field of an image \newinstance. + CImg get_structure_tensors(const bool is_fwbw_scheme=false) const { if (is_empty()) return *this; CImg res; if (_depth>1) { // 3d res.assign(_width,_height,_depth,6,0); - CImg_3x3x3(I,Tfloat); - switch (scheme) { - case 0 : { // classical central finite differences + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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, @@ -23752,31 +36236,13 @@ namespace cimg_library { *(ptrd5++)+=iz*iz; } } - } break; - case 1 : { // Forward/backward finite differences (version 1). - 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_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 + ixf*ixb + ixb*ixf + ixb*ixb)/4; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; - *(ptrd3++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4; - *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; - *(ptrd5++)+=(izf*izf + izf*izb + izb*izf + izb*izb)/4; - } - } - } break; - default : { // Forward/backward finite differences (version 2). + } else { // Forward/backward finite differences. + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=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, @@ -23790,15 +36256,14 @@ namespace cimg_library { *(ptrd5++)+=(izf*izf + izb*izb)/2; } } - } break; } } else { // 2d res.assign(_width,_height,_depth,3,0); - CImg_3x3(I,Tfloat); - switch (scheme) { - case 0 : { // classical central finite differences + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=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, @@ -23808,23 +36273,11 @@ namespace cimg_library { *(ptrd2++)+=iy*iy; } } - } break; - case 1 : { // Forward/backward finite differences (version 1). - 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_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 + ixf*ixb + ixb*iyf + ixb*ixb)/4; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4; - } - } - } break; - default : { // Forward/backward finite differences (version 2). + } else { // Forward/backward finite differences (version 2). + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=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, @@ -23834,221 +36287,374 @@ namespace cimg_library { *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; } } - } break; } } return res; } - //! Get a diffusion tensor for edge-preserving anisotropic smoothing of an image. - CImg& edge_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) { + //! 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& 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 res; - const float nsharpness = cimg::max(sharpness,1e-5f), power1 = (is_sqrt?0.5f:1)*nsharpness, power2 = power1/(1e-7f+1-anisotropy); + 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 - CImg val(3), vec(3,3); get_structure_tensors().move_to(res).blur(sigma); - 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_forXYZ(*this,x,y,z) { - 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; + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=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 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 - CImg val(2), vec(2,2); get_structure_tensors().move_to(res).blur(sigma); - Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); - cimg_forXY(*this,x,y) { - 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; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=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 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); } - CImg get_edge_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(*this,false).edge_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt); + //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance. + CImg 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(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt); } - //! Estimate a displacement field between specified source image and image instance. + //! Estimate displacement field between two images. /** - \param is_backward : if false, match I2(X+U(X)) = I1(X), else match I2(X) = I1(X-U(X)). + \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& displacement(const CImg& source, const float smoothness=0.1f, const float precision=5.0f, const unsigned int nb_scales=0, const unsigned int iteration_max=10000, - const bool is_backward=false) { - return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward).move_to(*this); + const bool is_backward=false, + const CImg& guide=CImg::const_empty()) { + return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). + move_to(*this); } - CImg get_displacement(const CImg& source, + //! Estimate displacement field between two images \newinstance. + CImg get_displacement(const CImg& source, const float smoothness=0.1f, const float precision=5.0f, const unsigned int nb_scales=0, const unsigned int iteration_max=10000, - const bool is_backward=false) const { - if (is_empty() || !source) return (+*this); + const bool is_backward=false, + const CImg& guide=CImg::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.", + "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 " + "displacement(): Invalid specified precision %g " "(should be >=0)", cimg_instance, precision); - const unsigned int _nb_scales = nb_scales>0?nb_scales:(unsigned int)(2*std::log((double)(cimg::max(_width,_height)))); + + 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._spectrum0?nb_scales: + (unsigned int)cimg::round(std::log(mins/8.0)/std::log(1.5),1,1); + const float _precision = (float)std::pow(10.0,-(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); - const bool is_3d = source._depth>1; - CImg U; - for (int scale = _nb_scales-1; scale>=0; --scale) { + CImg 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 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 U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); + 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::max(); const CImgList dI = is_backward?I1.get_gradient():I2.get_gradient(); for (unsigned int iteration = 0; iteration=0) cimg_for3XYZ(U,x,y,z) { // Isotropic regularization. - 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 deltaI = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c)); - else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c)); - cimg_forC(U,c) { + if (smoothness>=0) // Isotropic regularization. + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=8 && _width>=16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) 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; } - _energy+=deltaI*deltaI + smoothness*_energy_regul; - } else { + 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_for3XYZ(U,x,y,z) { // Anisotropic regularization. - 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 deltaI = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c)); - else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c)); - cimg_forC(U,c) { + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=8 && _width>=16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) 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; } - _energy+=deltaI*deltaI + 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) cimg_for3XY(U,x,y) { // Isotropic regularization. - 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 deltaI = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c)); - else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c)); - cimg_forC(U,c) { + if (smoothness>=0) // Isotropic regularization. + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) 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; } - _energy+=deltaI*deltaI + smoothness*_energy_regul; - } else { + 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_for3XY(U,x,y) { // Anisotropic regularization. - 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 deltaI = 0, _energy_regul = 0; - if (is_backward) cimg_forC(I2,c) deltaI+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c)); - else cimg_forC(I2,c) deltaI+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c)); - cimg_forC(U,c) { + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) 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; } - _energy+=deltaI*deltaI + 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; + } } } } @@ -24061,96 +36667,595 @@ namespace cimg_library { return U; } - //! Compute the distance transform according to a specified value. - /** The distance transform implementation has been submitted by A. Meijster, and implements + //! Compute correspondence map between two images, using the patch-match 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 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. + \note + The patch-match algorithm is described in this paper: + Connelly Barnes, Eli Shechtman, Adam Finkelstein, Dan B Goldman(2009), + PatchMatch: A Randomized Correspondence Algorithm for Structural Image Editing + **/ + template + CImg& patchmatch(const CImg& 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 CImg &guide, + CImg &matching_score) { + return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,guide,matching_score).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \newinstance. + template + CImg get_patchmatch(const CImg& 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 CImg &guide, + CImg &matching_score) const { + return _patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms, + guide,true,matching_score); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg& patchmatch(const CImg& 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 CImg &guide) { + return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,guide).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg get_patchmatch(const CImg& 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 CImg &guide) const { + return _patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms, + guide,false,CImg::empty()); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + CImg& patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth=1, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5) { + return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + CImg get_patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth=1, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5) const { + return _patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms, + CImg::const_empty(), + false,CImg::empty()); + } + + template + CImg _patchmatch(const CImg& 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 CImg &guide, + const bool is_matching_score, + CImg &matching_score) const { + if (is_empty()) return CImg::const_empty(); + if (patch_image._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "patchmatch(): 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 + "patchmatch(): 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 + "patchmatch(): 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 + "patchmatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions " + "considering instance and patch image 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 map(_width,_height,_depth,patch_image._depth>1?3:2); + CImg score(_width,_height,_depth); + 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::inf()); + } else cimg_forXYZ(*this,x,y,z) { // Random initialization. + const int + cx1 = x<=psizew1?x:(x::inf()); + } + + // Start iteration loop. + for (unsigned int iter = 0; iter64 && iter0) { // Compare with left neighbor. + const int 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=cy1 && v=cz1 && w0) { // Compare with up neighbor. + const int 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=cy1 - 1 && v=cz1 && w0) { // Compare with backward neighbor. + const int 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=cy1 && v=cz1 - 1 && w=cx1 + 1 && u=cy1 && v=cz1 && w=cx1 && u=cy1 + 1 && v=cz1 && w=cx1 && u=cy1 && v=cz1 + 1 && w::inf()); + } else cimg_forXY(*this,x,y) { // Random initialization. + const int + cx1 = x<=psizew1?x:(x::inf()); + } + + // Start iteration loop. + for (unsigned int iter = 0; iter64 && iter0) { // Compare with left neighbor. + const int u = map(x - 1,y,0), v = map(x - 1,y,1); + if (u>=cx1 - 1 && u=cy1 && v0) { // Compare with up neighbor. + const int u = map(x,y - 1,0), v = map(x,y - 1,1); + if (u>=cx1 && u=cy1 - 1 && v=cx1 + 1 && u=cy1 && v=cx1 && u=cy1 + 1 && v& img1, const CImg& img2, + const unsigned int psizew, const unsigned int psizeh, + const int x1, const int y1, + const int x2, const int y2, + const float max_ssd) { // 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 - psizeh*img1._width, + offy2 = (ulongT)img2._width*img2._height - psizeh*img2._width; + float ssd = 0; + cimg_forC(img1,c) { + for (unsigned int j = 0; jmax_ssd) return max_ssd; + p1+=offx1; p2+=offx2; + } + p1+=offy1; p2+=offy2; + } + return ssd; + } + + static float _patchmatch(const CImg& img1, const CImg& img2, + 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 float max_ssd) { // 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 - psizeh*img1._width - psizew, + offy2 = (ulongT)img2._width*img2._height - psizeh*img2._width - psizew, + offz1 = (ulongT)img1._width*img1._height*img1._depth - psized*img1._width*img1._height - + psizeh*img1._width - psizew, + offz2 = (ulongT)img2._width*img2._height*img2._depth - psized*img2._width*img2._height - + psizeh*img2._width - psizew; + float ssd = 0; + cimg_forC(img1,c) { + for (unsigned int k = 0; kmax_ssd) return max_ssd; + p1+=offx1; p2+=offx2; + } + p1+=offy1; p2+=offy2; + } + p1+=offz1; p2+=offz2; + } + return ssd; + } + + //! Compute Euclidean distance function to a specified value. + /** + \param value Reference value. + \param metric Type of metric. Can be { 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }. + \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& distance(const T value, const unsigned int metric=2) { + CImg& distance(const T& value, const unsigned int metric=2) { if (is_empty()) return *this; + if (cimg::type::string()!=cimg::type::string()) // For datatype < int. + return CImg(*this,false).distance((Tint)value,metric). + cut((Tint)cimg::type::min(),(Tint)cimg::type::max()).move_to(*this); bool is_value = false; - cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999); + 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::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); // Euclidean. - default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Squared Euclidean. + 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; } - CImg get_distance(const T value, const unsigned int metric=2) const { + //! Compute distance to a specified value \newinstance. + CImg get_distance(const T& value, const unsigned int metric=2) const { return CImg(*this,false).distance((Tfloat)value,metric); } - static long _distance_sep_edt(const long i, const long u, const long *const g) { - return (u*u-i*i+g[u]-g[i])/(2*(u-i)); + 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 long _distance_dist_edt(const long x, const long i, const long *const g) { - return (x-i)*(x-i) + g[i]; + static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) { + return (x - i)*(x - i) + g[i]; } - static long _distance_sep_mdt(const long i, const long u, const long *const g) { - return (u-i<=g[u]-g[i]?999999999:(g[u]-g[i]+u+i)/2); + 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 long _distance_dist_mdt(const long x, const long i, const long *const g) { - return (x=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; } if (q<0) { q = 0; s[0] = u; } - else { const long w = 1 + sep(s[q], u, g); if (w<(long)len) { ++q; s[q] = u; t[q] = w; }} + 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. + for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan. } - CImg& _distance_core(long (*const sep)(const long, const long, const long *const), - long (*const f)(const long, const long, const long *const)) { - const unsigned int wh = _width*_height; + CImg& _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 g(_width), dt(_width), s(_width), t(_width); CImg img = get_shared_channel(c); +#if defined(cimg_use_openmp) && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16) + firstprivate(g,dt,s,t)) +#endif cimg_forYZ(*this,y,z) { // Over X-direction. - cimg_forX(*this,x) g[x] = (long)img(x,y,z,0,wh); + 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]; } - g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height); - cimg_forXZ(*this,x,z) { // Over Y-direction. - cimg_forY(*this,y) g[y] = (long)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 (_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>=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>=512 && _width*_height>=16) + firstprivate(g,dt,s,t)) +#endif cimg_forXY(*this,x,y) { // Over Z-direction. - cimg_forZ(*this,z) g[z] = (long)img(x,y,z,0,wh); + 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]; } @@ -24159,19 +37264,23 @@ namespace cimg_library { return *this; } - //! Compute the chamfer distance transform according to a specified value, with a custom metric. + //! Compute chamfer distance to a specified value, with a custom metric. /** - The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé. - **/ + \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 - CImg& distance(const T value, const CImg& metric_mask) { + CImg& distance(const T& value, const CImg& metric_mask) { if (is_empty()) return *this; bool is_value = false; - cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999); + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999; if (!is_value) return fill(cimg::type::max()); - const unsigned long wh = _width*_height; + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) cimg_forC(*this,c) { CImg img = get_shared_channel(c); + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=1024)) cimg_forXYZ(metric_mask,dx,dy,dz) { const t weight = metric_mask(dx,dy,dz); if (weight) { @@ -24197,70 +37306,381 @@ namespace cimg_library { return *this; } + //! Compute chamfer distance to a specified value, with a custom metric \newinstance. template - CImg get_distance(const T value, const CImg& metric_mask) const { + CImg get_distance(const T& value, const CImg& metric_mask) const { return CImg(*this,false).distance(value,metric_mask); } - //! Compute the distance map to one specified point. - CImg& distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { - return get_distance_dijkstra(x,y,z).move_to(*this); + //! 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 + CImg& distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) { + return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); } - //! Compute the distance map to one specified point \newinstance. - CImg get_distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { - if (is_empty()) return *this; - if (!containsXYZC(x,y,z,0)) + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance. + template + CImg::type> + get_distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) const { + if (is_empty()) return return_path.assign(); + if (!is_sameXYZ(metric)) throw CImgArgumentException(_cimg_instance - "distance_dijkstra() : image instance does not contain specified starting point (%u,%u,%u).", + "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) " + "have incompatible dimensions.", cimg_instance, - x,y,z); - if (_spectrum!=1) - throw CImgInstanceException(_cimg_instance - "distance_dijkstra() : image instance is not a scalar image.", - cimg_instance); - CImg res(_width,_height,_depth,2); - CImg in_queue(_width,_height,_depth,1,0); - CImg Q; - unsigned int sizeQ = 0; + metric._width,metric._height,metric._depth,metric._spectrum); + typedef typename cimg::superset::type td; // Type used for computing cumulative distances. + CImg result(_width,_height,_depth,_spectrum), Q; + CImg is_queued(_width,_height,_depth,1); + if (return_path) return_path.assign(_width,_height,_depth,_spectrum); - // Put specified point in priority queue. - Q._priority_queue_insert(in_queue,sizeQ,0,x,y,z); - res(x,y,z) = 0; res(x,y,z,1) = 0; + cimg_forC(*this,c) { + const CImg img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg res = result.get_shared_channel(c); + CImg path = return_path?return_path.get_shared_channel(c):CImg(); + unsigned int sizeQ = 0; - // Start distance propagation. - while (sizeQ) { + // 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; + } - // 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 Tfloat potential = (Tfloat)-Q(0,0); - Q._priority_queue_remove(sizeQ); + // Start distance propagation. + while (sizeQ) { - // Update neighbors. - Tfloat npot = 0; - if (x-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x-1,y,z)+potential),x-1,y,z)) { - res(x-1,y,z) = npot; res(x-1,y,z,1) = 2; - } - if (x+1=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y-1,z)+potential),x,y-1,z)) { - res(x,y-1,z) = npot; res(x,y-1,z,1) = 4; - } - if (y+1=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z-1)+potential),x,y,z-1)) { - res(x,y,z-1) = npot; res(x,y,z+1,1) = 6; - } - if (z+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=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=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=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=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=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=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=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=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=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=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=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=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 + CImg& distance_dijkstra(const T& value, const CImg& 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 + CImg get_distance_dijkstra(const T& value, const CImg& metric, + const bool is_high_connectivity=false) const { + CImg 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 + CImg& distance_eikonal(const T& value, const CImg& 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 + CImg get_distance_eikonal(const T& value, const CImg& 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 result(_width,_height,_depth,_spectrum,cimg::type::max()), Q; + CImg 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 img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg 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=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=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=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=0 && state(x,y - 1,z)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); + if (dist=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, const Tfloat P, + const int x=0, const int y=0, const int z=0) const { + const Tfloat M = (Tfloat)cimg::type::max(); + T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 11) { // 3d. + T + T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1=0?res(x,y,z - 1):M,z + 1T2) 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 (T31) { // 2d. + T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1T2) cimg::swap(T1,T2); + if (P<=0) return (Tfloat)T1; + if (T2 + void _eik_priority_queue_insert(CImg& 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& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) { if (is_empty()) return *this; CImg velocity(*this); @@ -24277,7 +37697,7 @@ namespace cimg_library { 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 = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy + gz*gz)), + ng = 1e-5f + cimg::hypot(gx,gy,gz), ngx = gx/ng, ngy = gy/ng, ngz = gz/ng, @@ -24294,7 +37714,7 @@ namespace cimg_library { sgn = -cimg::sign(Icc), ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc), iy = gy*sgn>0?(Icn - Icc):(Icc - Icp), - ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy)), + ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)), ngx = gx/ng, ngy = gy/ng, veloc = sgn*(ngx*ix + ngy*iy - 1); @@ -24307,11 +37727,13 @@ namespace cimg_library { return *this; } - CImg get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) const { + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance. + CImg get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, + const float time_step=0.5f) const { return CImg(*this,false).distance_eikonal(nb_iterations,band_size,time_step); } - //! Compute the Haar multiscale wavelet transform (monodimensional version). + //! Compute Haar multiscale wavelet transform. /** \param axis Axis considered for the transform. \param invert Set inverse of direct transform. @@ -24321,18 +37743,19 @@ namespace cimg_library { return get_haar(axis,invert,nb_scales).move_to(*this); } + //! Compute Haar multiscale wavelet transform \newinstance. CImg get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { - if (is_empty() || !nb_scales) return (+*this); + if (is_empty() || !nb_scales) return +*this; CImg res; - + const Tfloat sqrt2 = std::sqrt(2.0f); if (nb_scales==1) { - switch (cimg::uncase(axis)) { // Single scale transform + switch (cimg::lowercase(axis)) { // Single scale transform case 'x' : { const unsigned int w = _width/2; if (w) { - if (w%2) + if ((w%2) && w!=1) throw CImgInstanceException(_cimg_instance - "haar() : Sub-image width %u is not even at scale %u.", + "haar(): Sub-image width %u is not even.", cimg_instance, w); @@ -24340,14 +37763,14 @@ namespace cimg_library { if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X for (unsigned int x = 0, xw = w, x2 = 0; x get_haar(const bool invert=false, const unsigned int nb_scales=1) const { CImg 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); } @@ -24482,41 +37909,48 @@ namespace cimg_library { 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 && s1) { - unsigned int w = _width, d = _depth; for (unsigned int s = 1; w && d && s1) { if (_depth>1) { - unsigned int h = _height, d = _depth; for (unsigned int s = 1; h && d && s1) { - unsigned int d = _depth; for (unsigned int s = 1; d && s1) { if (_height>1) { - if (_depth>1) for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s1) + for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s1) { - if (_depth>1) for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s1) + for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s1) for (unsigned int s = 1, d = _depth/2; d && s get_FFT(const char axis, const bool invert=false) const { + //! 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 get_FFT(const char axis, const bool is_invert=false) const { CImgList res(*this,CImg()); - CImg::FFT(res[0],res[1],axis,invert); + CImg::FFT(res[0],res[1],axis,is_invert); return res; } - //! Compute a n-d Fast-Fourier Transform. - CImgList get_FFT(const bool invert=false) const { + //! Compute n-d Fast Fourier Transform. + /* + \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + CImgList get_FFT(const bool is_invert=false) const { CImgList res(*this,CImg()); - CImg::FFT(res[0],res[1],invert); + CImg::FFT(res[0],res[1],is_invert); return res; } - //! Compute a 1d Fast Fourier Transform, along a specified axis. - static void FFT(CImg& real, CImg& imag, const char axis, const bool invert=false) { + //! 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& real, CImg& imag, const char axis, const bool is_invert=false) { if (!real) - throw CImgInstanceException("CImg<%s>::FFT() : Specified real part is empty.", + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.", pixel_type()); - if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0); + 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.", + 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::uncase(axis)) { + switch (cimg::lowercase(axis)) { case 'x' : { // Fourier along X, using FFTW library. data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width); - data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + 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 (invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); } + 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); - data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + 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); @@ -24606,67 +38070,72 @@ namespace cimg_library { 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 (invert) cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } + 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); - data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); - const unsigned int off = real._width*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 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 (invert) cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } + 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 : { // Fourier along C, using FFTW library. - data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._spectrum); - data_plan = fftw_plan_dft_1d(real._spectrum,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); - const unsigned int off = real._width*real._height*real._depth; - cimg_forXYZ(real,x,y,z) { - T *ptrr = real.data(x,y,z,0), *ptri = imag.data(x,y,z,0); - double *ptrd = (double*)data_in; - cimg_forC(real,c) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } - fftw_execute(data_plan); - const unsigned int fact = real._spectrum; - if (invert) cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); } - else cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } - } - } + 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::uncase(axis)) { + 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.", + 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; ii) 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=m; j-=m, m = n, n>>=1) {} } for (unsigned int delta = 2; delta<=N; delta<<=1) { - const unsigned int delta2 = (delta>>1); + const unsigned int delta2 = delta>>1; for (unsigned int i = 0; i>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.", + 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; ii) 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=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>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.", + 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; ii) 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=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::FFT() : Invalid specified axis '%c' for real and imaginary parts (%u,%u,%u,%u) " + 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); @@ -24776,28 +38256,53 @@ namespace cimg_library { #endif } - //! Compute a n-d Fast Fourier Transform. - static void FFT(CImg& real, CImg& imag, const bool invert=false) { + //! 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& real, CImg& imag, const bool is_invert=false, const unsigned int nb_threads=0) { if (!real) - throw CImgInstanceException("CImgList<%s>::FFT() : Empty specified real part.", + throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.", pixel_type()); - if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0); + 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.", + 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 unsigned int w = 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,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + 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; x1) FFT(real,imag,'z',invert); - if (real._height>1) FFT(real,imag,'y',invert); - if (real._width>1) FFT(real,imag,'x',invert); + 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 } @@ -24833,89 +38343,123 @@ namespace cimg_library { //@{ //------------------------------------- - //! Shift a 3d object. + //! 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& 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.", + "shift_object3d(): Instance is not a set of 3d vertices.", cimg_instance); - get_shared_line(0)+=tx; get_shared_line(1)+=ty; get_shared_line(2)+=tz; + get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz; return *this; } + //! Shift 3d object's vertices \newinstance. CImg get_shift_object3d(const float tx, const float ty=0, const float tz=0) const { return CImg(*this,false).shift_object3d(tx,ty,tz); } - //! Shift a 3d object so that it becomes centered. + //! Shift 3d object's vertices, so that it becomes centered. + /** + \note The object center is computed as its barycenter. + **/ CImg& shift_object3d() { if (_height!=3 || _depth>1 || _spectrum>1) throw CImgInstanceException(_cimg_instance - "shift_object3d() : Instance is not a set of 3d vertices.", + "shift_object3d(): Instance is not a set of 3d vertices.", cimg_instance); - CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); - float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm); + CImg 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 get_shift_object3d() const { return CImg(*this,false).shift_object3d(); } - //! Resize a 3d object. + //! 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& 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.", + "resize_object3d(): Instance is not a set of 3d vertices.", cimg_instance); - CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); - float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm); + CImg 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 (xm0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } if (ym0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } if (zm0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } return *this; } + //! Resize 3d object \newinstance. CImg get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { return CImg(*this,false).resize_object3d(sx,sy,sz); } - //! Resize a 3d object so that its max dimension if one. + //! Resize 3d object to unit size. CImg resize_object3d() { if (_height!=3 || _depth>1 || _spectrum>1) throw CImgInstanceException(_cimg_instance - "resize_object3d() : Instance is not a set of 3d vertices.", + "resize_object3d(): Instance is not a set of 3d vertices.", cimg_instance); - CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); - float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm); + CImg 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 get_resize_object3d() const { return CImg(*this,false).resize_object3d(); } - //! Append a 3d object to another one. + //! 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 - CImg& append_object3d(CImgList& primitives, const CImg& obj_vertices, const CImgList& obj_primitives) { + CImg& append_object3d(CImgList& primitives, const CImg& obj_vertices, + const CImgList& 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.", + "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); + 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.", + "append_object3d(): Instance is not a set of 3d vertices.", cimg_instance); const unsigned int P = _width; @@ -24936,30 +38480,36 @@ namespace cimg_library { } //! 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 const CImg& texturize_object3d(CImgList& primitives, CImgList& colors, - const CImg& texture, const CImg& coords=CImg::empty()) const { + const CImg& texture, const CImg& coords=CImg::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.", + "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).", + "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).", cimg_instance, coords._width,coords._height,coords._depth,coords._spectrum,coords._data); - CImg _coords; + CImg _coords; if (!coords) { // If no texture coordinates specified, do a default XY-projection. _coords.assign(_width,2); float - xmin, xmax = (float)get_shared_line(0).max_min(xmin), - ymin, ymax = (float)get_shared_line(1).max_min(ymin), + 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) = (unsigned int)(((*this)(p,0)-xmin)*(texture._width-1)/dx); - _coords(p,1) = (unsigned int)(((*this)(p,1)-ymin)*(texture._height-1)/dy); + _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; @@ -24969,36 +38519,40 @@ namespace cimg_library { const unsigned int siz = p.size(); switch (siz) { case 1 : { // Point. - const unsigned int - i0 = (unsigned int)p[0], - x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1); - texture.get_vector_at(x0,y0).move_to(colors[l]); + 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], - x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1), - x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1); - if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true); + 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::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], - x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1), - x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1), - x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1); - if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true); + 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::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], - x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1), - x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1), - x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1), - x3 = (unsigned int)_coords(i3,0), y3 = (unsigned int)_coords(i3,1); - if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true); + 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::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p); } break; } @@ -25006,19 +38560,19 @@ namespace cimg_library { return *this; } - //! Create and return a 3d elevation of the image instance. + //! 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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code const CImg img("reference.jpg"); CImgList faces3d; CImgList colors3d; - const CImg points3d = img.get_elevation3d(faces3d,colors,img.get_norm()*0.2); + const CImg points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2); CImg().display_object3d("Elevation3d",points3d,faces3d,colors3d); \endcode \image html ref_elevation3d.jpg @@ -25027,10 +38581,11 @@ namespace cimg_library { CImg get_elevation3d(CImgList& primitives, CImgList& colors, const CImg& 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) " + "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); + 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; @@ -25040,15 +38595,23 @@ namespace cimg_library { for (unsigned int x = 0; x1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r, - b = _spectrum>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(_spectrum>1?0:r); + 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::vector((tc)r,(tc)g,(tc)b).move_to(colors); } const typename CImg::_functor2d_int func(elevation); - return elevation3d(primitives,func,0,0,_width-1.0f,_height-1.0f,_width,_height); + return elevation3d(primitives,func,0,0,_width - 1.0f,_height - 1.0f,_width,_height); } - //! Create and return the 3d projection planes of the image instance. + //! 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 CImg get_projections3d(CImgList& primitives, CImgList& colors, const unsigned int x0, const unsigned int y0, const unsigned int z0, @@ -25061,22 +38624,27 @@ namespace cimg_library { _z0 = (z0>=_depth)?_depth - 1:z0; CImg 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); + ((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); + 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 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); + 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::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::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::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); + CImg::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::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::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); @@ -25084,15 +38652,15 @@ namespace cimg_library { return points; } - //! Create and return a isoline of the image instance as a 3d object. + //! 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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code const CImg img("reference.jpg"); CImgList faces3d; @@ -25106,34 +38674,35 @@ namespace cimg_library { 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.", + "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.", + "get_isoline3d(): Instance is not a 2d image.", cimg_instance); primitives.assign(); if (is_empty()) return *this; CImg 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.0f,height()-1.0f,width(),height()); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.0f,height() - 1.0f,width(),height()); } else { const _functor2d_float func(*this); - vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.0f,height() - 1.0f,size_x,size_y); } return vertices; } - //! Create and return a isosurface of the image instance as a 3d object. + //! 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 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 image (0<=i<=N-1). - \par Sample code : + \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 image (0<=i<=N - 1). + \par Example \code const CImg img = CImg("reference.jpg").resize(-100,-100,20); CImgList faces3d; @@ -25147,22 +38716,34 @@ namespace cimg_library { 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.", + "get_isosurface3d(): Instance is not a scalar image.", cimg_instance); primitives.assign(); if (is_empty()) return *this; CImg 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.0f,height()-1.0f,depth()-1.0f,width(),height(),depth()); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.0f,height() - 1.0f,depth() - 1.0f, + width(),height(),depth()); } else { const _functor3d_float func(*this); - vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,size_x,size_y,size_z); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.0f,height() - 1.0f,depth() - 1.0f, + size_x,size_y,size_z); } return vertices; } - //! Get elevation3d of a function. + //! 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 float (*func)(const float x,const float y). + \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 static CImg elevation3d(CImgList& primitives, const tfunc& func, const float x0, const float y0, const float x1, const float y1, @@ -25171,10 +38752,12 @@ namespace cimg_library { nx0 = x0=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; + _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).", + throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).", pixel_type(), nsize_x,nsize_y); @@ -25194,33 +38777,47 @@ namespace cimg_library { const unsigned int yw = y*nsize_x; for (unsigned int x = 0; x static CImg elevation3d(CImgList& primitives, const char *const expression, const float x0, const float y0, const float x1, const float y1, - const int sizex=256, const int sizey=256) { + const int size_x=256, const int size_y=256) { const _functor2d_expr func(expression); - return elevation3d(primitives,func,x0,y0,x1,y1,sizex,sizey); + return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y); } - //! Get isoline as a 3d object. + //! 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 float (*func)(const float x,const float y). + \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 static CImg isoline3d(CImgList& primitives, const tfunc& func, const float isovalue, const float x0, const float y0, const float x1, const float y1, - const int sizex=256, const int sizey=256) { - static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; + 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)(sizex>=0?sizex:cimg::round((x1-x0)*-sizex/100 + 1)), - _ny = (unsigned int)(sizey>=0?sizey:cimg::round((y1-y0)*-sizey/100 + 1)), + _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, @@ -25249,35 +38846,36 @@ namespace cimg_library { val2 = values2(nxi) = (float)func(nX,nY), val3 = values2(xi) = (float)func(X,nY); const unsigned int - configuration = (val0::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; + indices1(nxi,1) = vertices.width(); CImg::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; + indices2(xi,0) = vertices.width(); CImg::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; + indices1(xi,1) = vertices.width(); CImg::vector(X,Yi,0).move_to(vertices); } // Create segments for (const int *segment = segments[configuration]; *segment!=-1; ) { - const unsigned int p0 = *(segment++), p1 = *(segment++); + 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)); @@ -25291,12 +38889,13 @@ namespace cimg_library { return vertices>'x'; } + //! Compute isolines of a function, as a 3d object \overloading. template static CImg isoline3d(CImgList& primitives, const char *const expression, const float isovalue, const float x0, const float y0, const float x1, const float y1, - const int sizex=256, const int sizey=256) { + const int size_x=256, const int size_y=256) { const _functor2d_expr func(expression); - return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,sizex,sizey); + return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y); } template @@ -25311,13 +38910,28 @@ namespace cimg_library { return 0; } - //! Get isosurface as a 3d object. + //! 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 float (*func)(const float x, const float y, const float z). + \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 static CImg isosurface3d(CImgList& 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 unsigned int edges[256] = { + 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, @@ -25333,143 +38947,272 @@ namespace cimg_library { 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 }; + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 + }; - static 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 } + 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_y:cimg::round((z1-z0)*-size_z/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, @@ -25513,76 +39256,79 @@ namespace cimg_library { val7 = values2(xi,nyi) = (float)func(X,nY,nZ); const unsigned int configuration = - (val0::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; + indices1(nxi,yi,1) = vertices.width(); CImg::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; + indices1(xi,nyi,0) = vertices.width(); CImg::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; + indices1(xi,yi,1) = vertices.width(); CImg::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; + indices2(xi,yi,0) = vertices.width(); CImg::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; + indices2(nxi,yi,1) = vertices.width(); CImg::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; + indices2(xi,nyi,0) = vertices.width(); CImg::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; + indices2(xi,yi,1) = vertices.width(); CImg::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; + indices1(xi,yi,2) = vertices.width(); CImg::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; + indices1(nxi,yi,2) = vertices.width(); CImg::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; + indices1(nxi,nyi,2) = vertices.width(); CImg::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; + indices1(xi,nyi,2) = vertices.width(); CImg::vector(X,nY,Zi).move_to(vertices); } // Create triangles for (const int *triangle = triangles[configuration]; *triangle!=-1; ) { - const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++); + 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)), @@ -25598,6 +39344,7 @@ namespace cimg_library { return vertices>'x'; } + //! Compute isosurface of a function, as a 3d object \overloading. template static CImg isosurface3d(CImgList& primitives, const char *const expression, const float isovalue, const float x0, const float y0, const float z0, @@ -25609,7 +39356,8 @@ namespace cimg_library { template static int _isosurface3d_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, - const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) { + 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); @@ -25646,10 +39394,12 @@ namespace cimg_library { struct _functor2d_expr { _cimg_math_parser *mp; - _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,0); } - ~_functor2d_expr() { delete mp; } + ~_functor2d_expr() { mp->end(); delete mp; } + _functor2d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } float operator()(const float x, const float y) const { - return (float)mp->eval(x,y,0,0); + return (float)(*mp)(x,y,0,0); } }; @@ -25671,10 +39421,12 @@ namespace cimg_library { struct _functor3d_expr { _cimg_math_parser *mp; - ~_functor3d_expr() { delete mp; } - _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,0); } + ~_functor3d_expr() { mp->end(); delete mp; } + _functor3d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } float operator()(const float x, const float y, const float z) const { - return (float)mp->eval(x,y,z,0); + return (float)(*mp)(x,y,z,0); } }; @@ -25686,15 +39438,15 @@ namespace cimg_library { } }; - //! Create and return a 3d box object. + //! 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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code CImgList faces3d; const CImg points3d = CImg::box3d(faces3d,10,20,30); @@ -25712,15 +39464,15 @@ namespace cimg_library { 0., 0., 0., 0.,size_z,size_z,size_z,size_z); } - //! Create and return a 3d cone. + //! 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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code CImgList faces3d; const CImg points3d = CImg::cone3d(faces3d,50); @@ -25742,22 +39494,22 @@ namespace cimg_library { } const unsigned int nbr = vertices._width - 2; for (unsigned int p = 0; p::vector(1,next,curr).move_to(primitives); CImg::vector(0,curr,next).move_to(primitives); } return vertices>'x'; } - //! Create and return a 3d cylinder. + //! 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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code CImgList faces3d; const CImg points3d = CImg::cylinder3d(faces3d,50); @@ -25780,15 +39532,15 @@ namespace cimg_library { } const unsigned int nbr = (vertices._width - 2)/2; for (unsigned int p = 0; p::vector(0,next,curr).move_to(primitives); - CImg::vector(1,curr+1,next+1).move_to(primitives); - CImg::vector(curr,next,next+1,curr+1).move_to(primitives); + CImg::vector(1,curr + 1,next + 1).move_to(primitives); + CImg::vector(curr,next,next + 1,curr + 1).move_to(primitives); } return vertices>'x'; } - //! Create and return a 3d torus. + //! 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). @@ -25796,8 +39548,8 @@ namespace cimg_library { \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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code CImgList faces3d; const CImg points3d = CImg::torus3d(faces3d,20,4); @@ -25827,17 +39579,16 @@ namespace cimg_library { } } for (unsigned int vv = 0; vv::vector(svv+nu,svv+uu,snv+uu).move_to(primitives); - CImg::vector(svv+nu,snv+uu,snv+nu).move_to(primitives); + const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv; + CImg::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives); } } return vertices>'x'; } - //! Create and return a 3d XY-plane. + //! 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). @@ -25845,8 +39596,8 @@ namespace cimg_library { \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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code CImgList faces3d; const CImg points3d = CImg::plane3d(faces3d,100,50); @@ -25866,20 +39617,20 @@ namespace cimg_library { for (unsigned int y = 0; y::vector(fx*x,fy*y,0).move_to(vertices); for (unsigned int y = 0; y::vector(off1,off4,off3,off2).move_to(primitives); } return vertices>'x'; } - //! Create and return a 3d sphere. + //! 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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code CImgList faces3d; const CImg points3d = CImg::sphere3d(faces3d,100,4); @@ -25893,7 +39644,7 @@ namespace cimg_library { // Create initial icosahedron primitives.assign(); - const double tmp = (1+std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1+tmp*tmp), b = tmp*a; + const double tmp = (1 + std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1 + tmp*tmp), b = tmp*a; CImgList vertices(12,1,3,1,1, b,a,0.0, -b,a,0.0, -b,-a,0.0, b,-a,0.0, a,0.0,b, a,0.0,-b, -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.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, @@ -25914,9 +39665,12 @@ namespace cimg_library { 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 = (float)std::sqrt(tnx0*tnx0+tny0*tny0+tnz0*tnz0), - tnx1 = (x0+x2)/2, tny1 = (y0+y2)/2, tnz1 = (z0+z2)/2, nn1 = (float)std::sqrt(tnx1*tnx1+tny1*tny1+tnz1*tnz1), - tnx2 = (x1+x2)/2, tny2 = (y1+y2)/2, tnz2 = (z1+z2)/2, nn2 = (float)std::sqrt(tnx2*tnx2+tny2*tny2+tnz2*tnz2), + 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; @@ -25927,9 +39681,9 @@ namespace cimg_library { if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices._width - 1; } - if (i1<0) { CImg::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices._width - 1; } - if (i2<0) { CImg::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices._width - 1; } + if (i0<0) { CImg::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; } + if (i1<0) { CImg::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; } + if (i2<0) { CImg::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; } primitives.remove(0); CImg::vector(p0,i0,i1).move_to(primitives); CImg::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives); @@ -25940,14 +39694,14 @@ namespace cimg_library { return (vertices>'x')*=radius; } - //! Create and return a 3d ellipsoid. + //! 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 image (0<=i<=N-1). - \par Sample code : + \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg image (0<=i<=N - 1). + \par Example \code CImgList faces3d; const CImg tensor = CImg::diagonal(10,7,3), @@ -25970,44 +39724,58 @@ namespace cimg_library { 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 vertices = sphere3d(primitives,1.0,subdivisions); - vertices.get_shared_line(0)*=l0; - vertices.get_shared_line(1)*=l1; - vertices.get_shared_line(2)*=l2; + vertices.get_shared_row(0)*=l0; + vertices.get_shared_row(1)*=l1; + vertices.get_shared_row(2)*=l2; return V*vertices; } - //! Convert a 3d object into a CImg3d. + //! 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 CImg& object3dtoCImg3d(const CImgList& primitives, const CImgList& colors, - const to& opacities) { - return get_object3dtoCImg3d(primitives,colors,opacities).move_to(*this); + 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 CImg& object3dtoCImg3d(const CImgList& primitives, - const CImgList& colors) { - return get_object3dtoCImg3d(primitives,colors).move_to(*this); + const CImgList& 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 - CImg& object3dtoCImg3d(const CImgList& primitives) { - return get_object3dtoCImg3d(primitives).move_to(*this); + CImg& object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,full_check).move_to(*this); } - CImg& object3dtoCImg3d() { - return get_object3dtoCImg3d().move_to(*this); + //! Convert 3d object into a CImg3d representation \overloading. + CImg& object3dtoCImg3d(const bool full_check=true) { + return get_object3dtoCImg3d(full_check).move_to(*this); } + //! Convert 3d object into a CImg3d representation \newinstance. template CImg get_object3dtoCImg3d(const CImgList& primitives, const CImgList& colors, - const to& opacities) const { - char error_message[1024] = { 0 }; - if (!is_object3d(primitives,colors,opacities,true,error_message)) + const to& opacities, + const bool full_check=true) const { + CImg 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); + "object3dtoCImg3d(): Invalid specified 3d object (%u,%u) (%s).", + cimg_instance,_width,primitives._width,error_message.data()); CImg res(1,_size_object3dtoCImg3d(primitives,colors,opacities)); float *ptrd = res._data; @@ -26036,7 +39804,7 @@ namespace cimg_library { } // Put color/texture data. - const unsigned int csiz = cimg::min(colors._width,primitives._width); + const unsigned int csiz = std::min(colors._width,primitives._width); for (int c = 0; c<(int)csiz; ++c) { const CImg& color = colors[c]; const tc *ptrc = color._data; @@ -26057,7 +39825,7 @@ namespace cimg_library { } } } - const int csiz2 = primitives._width - colors._width; + const int csiz2 = primitives.width() - colors.width(); for (int c = 0; c& primitives, const CImgList& colors, const CImgList& opacities) const { - unsigned int siz = 8 + 3*width(); + unsigned int siz = 8U + 3*_width; cimglist_for(primitives,p) siz+=primitives[p].size() + 1; - for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) { + 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; } + else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; } } if (colors._width& primitives, const CImgList& colors, const CImg& opacities) const { - unsigned int siz = 8 + 3*width(); + unsigned int siz = 8U + 3*_width; cimglist_for(primitives,p) siz+=primitives[p].size() + 1; - for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) { - const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; + 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 CImg get_object3dtoCImg3d(const CImgList& primitives, - const CImgList& colors) const { + const CImgList& colors, + const bool full_check=true) const { CImgList opacities; - return get_object3dtoCImg3d(primitives,colors,opacities); + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); } + //! Convert 3d object into a CImg3d representation \overloading. template - CImg get_object3dtoCImg3d(const CImgList& primitives) const { + CImg get_object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) const { CImgList colors, opacities; - return get_object3dtoCImg3d(primitives,colors,opacities); + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); } - CImg get_object3dtoCImg3d() const { + //! Convert 3d object into a CImg3d representation \overloading. + CImg get_object3dtoCImg3d(const bool full_check=true) const { CImgList opacities, colors; CImgList primitives(width(),1,1,1,1); cimglist_for(primitives,p) primitives(p,0) = p; - return get_object3dtoCImg3d(primitives,colors,opacities); + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); } - //! Convert a CImg3d (one-column image) into a 3d object. + //! 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 - CImg get_CImg3dtoobject3d(CImgList& primitives, CImgList& colors, CImgList& opacities) const { - char error_message[1024] = { 0 }; - if (!is_CImg3d(true,error_message)) + CImg& CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& 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 + CImg get_CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& opacities, + const bool full_check=true) const { + CImg 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); + "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++)), @@ -26176,7 +39967,7 @@ namespace cimg_library { } colors.assign(nb_primitives); cimglist_for(colors,c) { - if ((int)*ptrs==-128) { + 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); @@ -26185,7 +39976,7 @@ namespace cimg_library { } opacities.assign(nb_primitives); cimglist_for(opacities,o) { - if ((int)*ptrs==-128) { + 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); @@ -26195,11 +39986,6 @@ namespace cimg_library { return points; } - template - CImg& CImg3dtoobject3d(CImgList& primitives, CImgList& colors, CImgList& opacities) { - return get_CImg3dtoobject3d(primitives,colors,opacities).move_to(*this); - } - //@} //--------------------------- // @@ -26207,77 +39993,77 @@ namespace cimg_library { //@{ //--------------------------- - // The following _draw_scanline() routines are *non user-friendly functions*, used only for internal purpose. - // Pre-requisites : x0 CImg& _draw_scanline(const int x0, const int x1, const int y, - const tc *const color, const float opacity=1, - const float brightness=1, const bool init=false) { - static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); - static float nopacity = 0, copacity = 0; - static unsigned int whd = 0; - static const tc *col = 0; - if (init) { - nopacity = cimg::abs(opacity); - copacity = 1 - cimg::max(opacity,0); - whd = _width*_height*_depth; - } else { - const int nx0 = x0>0?x0:0, nx1 = x1=0) { - col = color; - const unsigned int 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 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::max(),(T)cimg::type::max()); + const int nx0 = x0>0?x0:0, nx1 = x1=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); + std::memset(ptrd,(int)val,dx + 1); ptrd+=whd; } - } else if (brightness<1) { // Brightness<1 - if (sizeof(T)!=1) cimg_forC(*this,c) { + } 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); + 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); + } 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); + 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 { // ** Transparent drawing ** - if (brightness==1) { // Brightness==1 - cimg_forC(*this,c) { - const T val = (T)*(col++); - for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } - ptrd+=off; - } - } else if (brightness<=1) { // Brightness<1 - cimg_forC(*this,c) { - const T val = (T)(*(col++)*brightness); - for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } - ptrd+=off; - } - } else { // Brightness>1 - cimg_forC(*this,c) { - const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); - for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *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; } } } @@ -26285,19 +40071,14 @@ namespace cimg_library { return *this; } - template - CImg& _draw_scanline(const tc *const color, const float opacity=1) { - return _draw_scanline(0,0,0,color,opacity,0,true); - } - - //! Draw a 2d colored point (pixel). + //! Draw a 3d point. /** \param x0 X-coordinate of the point. \param y0 Y-coordinate of the point. - \param color Pointer to \c spectrum() consecutive values, defining the color values. - \param opacity Drawing opacity (optional). + \param z0 Z-coordinate of the point. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. \note - - Clipping is supported. - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. \par Example: \code @@ -26307,24 +40088,16 @@ namespace cimg_library { \endcode **/ template - CImg& 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 3d colored point (voxel). - template CImg& 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).", + "draw_point(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } @@ -26333,7 +40106,19 @@ namespace cimg_library { return *this; } - // Draw a cloud of colored points. + //! Draw a 2d point \simplification. + template + CImg& 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 CImg& draw_point(const CImg& points, const tc *const color, const float opacity=1) { @@ -26341,7 +40126,7 @@ namespace cimg_library { switch (points._height) { case 0 : case 1 : throw CImgArgumentException(_cimg_instance - "draw_point() : Invalid specified point set (%u,%u,%u,%u,%p).", + "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 : { @@ -26354,18 +40139,17 @@ namespace cimg_library { return *this; } - //! Draw a 2d colored line. + //! 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 (optional). - \param pattern An integer whose bits describe the line pattern (optional). - \param init_hatch Flag telling if a reinitialization of the hash state must be done (optional). + \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 - - Clipping is supported. - Line routine uses Bresenham's algorithm. - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. \par Example: @@ -26380,12 +40164,11 @@ namespace cimg_library { 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).", + "draw_line(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); const bool xdir = x0=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 (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; } + 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 int - offx = (nx0=1) { if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { @@ -26424,7 +40213,7 @@ namespace cimg_library { if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { if (pattern&hatch) { T *ptrd = ptrd0; const tc* col = color; @@ -26434,7 +40223,8 @@ namespace cimg_library { 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; } + 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; } } @@ -26442,7 +40232,20 @@ namespace cimg_library { return *this; } - //! Draw a 2d colored line, with z-buffering. + //! 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 CImg& draw_line(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -26450,17 +40253,17 @@ namespace cimg_library { const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0) return *this; if (!color) throw CImgArgumentException(_cimg_instance - "draw_line() : Specified color is (null).", + "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.", + "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 (is_empty() || z0<=0 || z1<=0) return *this; static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); const bool xdir = x0dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const int - offx = (nx00?dx:1; + const longT + offx = (longT)(nx00?dx:1); if (opacity>=1) { if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { const tzfloat z = Z0 + x*dz/ndx; @@ -26534,7 +40338,7 @@ namespace cimg_library { if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } } else { - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); 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) { @@ -26559,34 +40363,76 @@ namespace cimg_library { return *this; } - //! Draw a 3d colored line. + //! 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 CImg& 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).", + "draw_line(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; 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.0f + nx1 - nx0; ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D); nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D); nx0 = 0; } - if (nx1>=width()) { const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0; ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); nx1 = width() - 1; } + if (nx0<0) { + const float D = 1.0f + nx1 - nx0; + ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D); + nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D); + nx0 = 0; + } + if (nx1>=width()) { + const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0; + ny1+=(int)(d*(1.0f + ny0 - ny1)/D); + nz1+=(int)(d*(1.0f + 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.0f + ny1 - ny0; nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D); nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D); ny0 = 0; } - if (ny1>=height()) { const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); ny1 = height() - 1; } + if (ny0<0) { + const float D = 1.0f + ny1 - ny0; + nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D); + nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D); + ny0 = 0; + } + if (ny1>=height()) { + const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0; + nx1+=(int)(d*(1.0f + nx0 - nx1)/D); + nz1+=(int)(d*(1.0f + 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.0f + nz1 - nz0; nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D); ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D); nz0 = 0; } - if (nz1>=depth()) { const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1 = depth() - 1; } - const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0), whd = _width*_height*_depth; + if (nz0<0) { + const float D = 1.0f + nz1 - nz0; + nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D); + ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D); + nz0 = 0; + } + if (nz1>=depth()) { + const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0; + nx1+=(int)(d*(1.0f + nx0 - nx1)/D); + ny1+=(int)(d*(1.0f + 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) { @@ -26596,7 +40442,7 @@ namespace cimg_library { } 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 - cimg::max(opacity,0); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); 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); @@ -26608,7 +40454,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d textured line. + //! Draw a textured 2d line. /** \param x0 X-coordinate of the starting line point. \param y0 Y-coordinate of the starting line point. @@ -26619,11 +40465,10 @@ namespace cimg_library { \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 (optional). - \param pattern An integer whose bits describe the line pattern (optional). - \param init_hatch Flag telling if the hash variable must be reinitialized (optional). + \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 - - Clipping is supported but not for texture coordinates. - Line routine uses the well known Bresenham's algorithm. \par Example: \code @@ -26640,13 +40485,12 @@ namespace cimg_library { 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).", + "draw_line(): 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_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); @@ -26693,17 +40537,21 @@ namespace cimg_library { int dx = xright - xleft, dy = ydown - yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const int - offx = (nx00?dx:1; + const longT + offx = (longT)(nx00?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; - cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; } + 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; @@ -26711,17 +40559,19 @@ namespace cimg_library { } 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; - cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; } + 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 - cimg::max(opacity,0); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); 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; - cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; } + 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; @@ -26729,7 +40579,8 @@ namespace cimg_library { } 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; - cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; } + 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; } } @@ -26737,7 +40588,23 @@ namespace cimg_library { return *this; } - //! Draw a 2d textured line, with perspective correction. + //! 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 CImg& draw_line(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, @@ -26746,14 +40613,14 @@ namespace cimg_library { 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).", + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", cimg_instance, texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - - if (is_empty() && z0<=0 && z1<=0) return *this; - if (is_overlapped(texture)) return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + 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 = x0dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); - const int - offx = (nx00?dx:1; + const longT + offx = (longT)(nx00?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; - T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; } + 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; - T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; } + 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 - cimg::max(opacity,0); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); 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; - T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; } + 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*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; } + cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } @@ -26852,7 +40729,24 @@ namespace cimg_library { return *this; } - //! Draw a 2d textured line, with z-buffering and perspective correction. + //! 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 CImg& draw_line(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -26863,20 +40757,20 @@ namespace cimg_library { const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { typedef typename cimg::superset::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.", + "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).", + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", cimg_instance, texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - - if (is_empty() || z0<=0 || z1<=0) return *this; - if (is_overlapped(texture)) return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + 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=height()) return *this; if (yup<0) { @@ -26934,18 +40828,21 @@ namespace cimg_library { zdown-=d*(zdown - zup)/D; txdown-=d*(txdown - txup)/D; tydown-=d*(tydown - tyup)/D; - ydown = height()-1; + 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 int - offx = (nx00?dx:1; + const longT + offx = (longT)(nx00?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) { @@ -26953,7 +40850,9 @@ namespace cimg_library { if (z>=(tzfloat)*ptrz) { *ptrz = (tz)z; const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; } + 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); @@ -26964,20 +40863,24 @@ namespace cimg_library { if (z>=(tzfloat)*ptrz) { *ptrz = (tz)z; const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; } + 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 - cimg::max(opacity,0); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); 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; - T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; } + 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); @@ -26988,36 +40891,27 @@ namespace cimg_library { if (z>=(tzfloat)*ptrz) { *ptrz = (tz)z; const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; - T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; } + 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+=offx; error+=dx; } + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } } } return *this; } - //! Draw a set of consecutive colored lines in the image instance. + //! 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 (optional). - \param pattern An integer whose bits describe the line pattern (optional). + \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. - \par Example: - \code - CImg img(100,100,1,3,0); - const unsigned char color[] = { 255,128,64 }; - CImgList points; - points.insert(CImg::vector(0,0)). - .insert(CImg::vector(70,10)). - .insert(CImg::vector(80,60)). - .insert(CImg::vector(10,90)); - img.draw_line(points,color); - \endcode **/ template CImg& draw_line(const CImg& points, @@ -27028,7 +40922,7 @@ namespace cimg_library { switch (points._height) { case 0 : case 1 : throw CImgArgumentException(_cimg_instance - "draw_line() : Invalid specified point set (%u,%u,%u,%u,%p).", + "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", cimg_instance, points._width,points._height,points._depth,points._spectrum,points._data); @@ -27056,19 +40950,17 @@ namespace cimg_library { return *this; } - //! Draw a colored arrow in the image instance. + //! 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 (optional). - \param length Length of the arrow head. If negative, describes a percentage of the arrow length (optional). - \param opacity Drawing opacity (optional). - \param pattern An integer whose bits describe the line pattern (optional). - \note - - Clipping is supported. + \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 CImg& draw_arrow(const int x0, const int y0, @@ -27087,13 +40979,13 @@ namespace cimg_library { 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; + 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 cubic spline curve in the image instance. + //! Draw a 2d spline. /** \param x0 X-coordinate of the starting curve point \param y0 Y-coordinate of the starting curve point @@ -27104,19 +40996,21 @@ namespace cimg_library { \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 (optional). - \param opacity Drawing opacity (optional). - \param pattern An integer whose bits describe the line pattern (optional). + \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 + - 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 + 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 @@ -27131,11 +41025,11 @@ namespace cimg_library { 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).", + "draw_spline(): Specified color is (null).", cimg_instance); - if (is_empty()) return *this; if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity); bool ninit_hatch = init_hatch; const float @@ -27143,7 +41037,7 @@ namespace cimg_library { bx = 3*(x1 - x0) - 2*u0 - u1, ay = v0 + v1 + 2*(y0 - y1), by = 3*(y1 - y0) - 2*v0 - v1, - _precision = 1/(std::sqrt(cimg::sqr((float)x0-x1)+cimg::sqr((float)y0-y1))*(precision>0?precision:1)); + _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; @@ -27157,7 +41051,7 @@ namespace cimg_library { return draw_line(ox,oy,x1,y1,color,opacity,pattern,false); } - //! Draw a cubic spline curve in the image instance (for volumetric images). + //! Draw a 3d spline \overloading. /** \note - Similar to CImg::draw_spline() for a 3d spline in a volumetric image. @@ -27168,11 +41062,11 @@ namespace cimg_library { 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).", + "draw_spline(): Specified color is (null).", cimg_instance); - if (is_empty()) return *this; if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity); bool ninit_hatch = init_hatch; const float @@ -27182,7 +41076,7 @@ namespace cimg_library { by = 3*(y1 - y0) - 2*v0 - v1, az = w0 + w1 + 2*(z0 - z1), bz = 3*(z1 - z0) - 2*w0 - w1, - _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1)); + _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; @@ -27197,7 +41091,7 @@ namespace cimg_library { return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false); } - //! Draw a cubic spline curve in the image instance. + //! Draw a textured 2d spline. /** \param x0 X-coordinate of the starting curve point \param y0 Y-coordinate of the starting curve point @@ -27212,9 +41106,9 @@ namespace cimg_library { \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 (optional). - \param opacity Drawing opacity (optional). - \param pattern An integer whose bits describe the line pattern (optional). + \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 @@ -27227,27 +41121,30 @@ namespace cimg_library { const bool init_hatch=true) { if (texture._depth>1 || texture._spectrum<_spectrum) throw CImgArgumentException(_cimg_instance - "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).", + "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,y0),opacity); + 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/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1)); + _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 t = 0; t<1; t+=_precision) { - const float t2 = t*t, t3 = t2*t; + 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*t + x0), - ny = (int)(ay*t3 + by*t2 + v0*t + y0), - ntx = tx0 + (int)((tx1-tx0)*t), - nty = ty0 + (int)((ty1-ty0)*t); + 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; @@ -27255,18 +41152,28 @@ namespace cimg_library { return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false); } - // Draw a set of connected spline curves in the image instance (internal). + //! 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 CImg& draw_spline(const CImg& points, const CImg& tangents, const tc *const color, const float opacity=1, - const bool close_set=false, const float precision=4, + 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).", + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", cimg_instance, points._width,points._height,points._depth,points._spectrum,points._data); @@ -27282,7 +41189,7 @@ namespace cimg_library { ninit_hatch = false; ox = x; oy = y; ou = u; ov = v; } - if (close_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false); + 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); @@ -27296,32 +41203,35 @@ namespace cimg_library { ninit_hatch = false; ox = x; oy = y; oz = z; ou = u; ov = v; ow = w; } - if (close_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false); + 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 colored splines in the image instance. + //! Draw a set of consecutive splines \overloading. + /** + Similar to previous function, with the point tangents automatically estimated from the given points set. + **/ template CImg& draw_spline(const CImg& points, const tc *const color, const float opacity=1, - const bool close_set=false, const float precision=4, + 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 tangents; switch (points._height) { case 0 : case 1 : throw CImgArgumentException(_cimg_instance - "draw_spline() : Invalid specified point set (%u,%u,%u,%u,%p).", + "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 = close_set?(p+points._width-1)%points._width:(p?p-1:0), - p1 = close_set?(p+1)%points._width:(p+1=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)), \ + 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), \ + _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, \ - cimg::min((int)(img)._height-y-1,y2-y)), \ + std::min((int)(img)._height - y - 1,y2 - y)), \ _errn = _dyn/2, \ _errr = _dyr/2, \ _errl = _dyl/2, \ @@ -27413,19 +41323,19 @@ namespace cimg_library { #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)), \ + 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), \ + _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, \ @@ -27435,14 +41345,14 @@ namespace cimg_library { _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ - cimg::min((int)(img)._height-y-1,y2-y)), \ + 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, \ + _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): \ @@ -27457,24 +41367,24 @@ namespace cimg_library { #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)), \ + 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), \ + _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, \ @@ -27487,21 +41397,21 @@ namespace cimg_library { _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ - cimg::min((int)(img)._height-y-1,y2-y)), \ + 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): \ + _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): \ + _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): \ + _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), \ @@ -27512,36 +41422,36 @@ namespace cimg_library { _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)) + _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)), \ + 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, \ + _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, \ @@ -27554,25 +41464,25 @@ namespace cimg_library { _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ - cimg::min((int)(img)._height-y-1,y2-y)), \ + 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): \ + _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): \ + _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): \ + _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): \ + _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), \ @@ -27586,38 +41496,39 @@ namespace cimg_library { (_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)) + _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) \ +#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)), \ + 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), \ + _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, \ @@ -27633,29 +41544,29 @@ namespace cimg_library { _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \ _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \ _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \ - cimg::min((int)(img)._height-y-1,y2-y)), \ + 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): \ + _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): \ + _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): \ + _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): \ + _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): \ + _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), \ @@ -27672,66 +41583,102 @@ namespace cimg_library { _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)) + _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl)) - // Draw a colored triangle (inner routine, uses bresenham's algorithm). + // [internal] Draw a filled triangle. template CImg& _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) { - _draw_scanline(color,opacity); - const float nbrightness = brightness<0?0:(brightness>2?2: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=0) { if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0) - _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness); + _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) _draw_scanline(xr,xl,y,color,opacity,nbrightness); + _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 2d filled colored triangle. + //! 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 CImg& 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).", + "draw_triangle(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); return *this; } - //! Draw a 2d outlined colored triangle. + //! 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 CImg& 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).", + "draw_triangle(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; 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 2d filled colored triangle, with z-buffering. + //! 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 CImg& draw_triangle(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -27740,22 +41687,22 @@ namespace cimg_library { const tc *const color, const float opacity=1, const float brightness=1) { typedef typename cimg::superset::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).", + "draw_triangle(): 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.", + "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_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + static const T maxval = (T)std::min(cimg::type::max(),(T)cimg::type::max()); const float - nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), - nbrightness = brightness<0?0:(brightness>2?2:brightness); - const int whd = _width*_height*_depth, offx = _spectrum*whd; + nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f), + 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); @@ -27777,9 +41724,9 @@ namespace cimg_library { 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; + if (xright>=width() - 1) xright = width() - 1; T* ptrd = data(xleft,y,0,0); - tz *ptrz = zbuffer.data(xleft,y); + 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) { @@ -27798,7 +41745,8 @@ namespace cimg_library { } 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; } + const tc *col = color; + cimg_forC(*this,c) { *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); ptrd+=whd; } ptrd-=offx; } zleft+=pentez; @@ -27814,7 +41762,8 @@ namespace cimg_library { } 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; } + const tc *col = color; + cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; } ptrd-=offx; } zleft+=pentez; @@ -27823,7 +41772,7 @@ namespace cimg_library { *ptrz = (tz)zleft; const tc *col = color; cimg_forC(*this,c) { - const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whd; } @@ -27837,20 +41786,19 @@ namespace cimg_library { return *this; } - //! Draw a 2d Gouraud-shaded colored triangle. + //! Draw a Gouraud-shaded 2d triangle. /** - \param x0 = X-coordinate of the first corner in the image instance. - \param y0 = Y-coordinate of the first corner in the image instance. - \param x1 = X-coordinate of the second corner in the image instance. - \param y1 = Y-coordinate of the second corner in the image instance. - \param x2 = X-coordinate of the third corner in the image instance. - \param y2 = Y-coordinate of the third corner in the image instance. - \param color = array of spectrum() values of type \c T, defining the global drawing color. - \param brightness0 = brightness of the first corner (in [0,2]). - \param brightness1 = brightness of the second corner (in [0,2]). - \param brightness2 = brightness of the third corner (in [0,2]). - \param opacity = opacity of the drawing. - \note Clipping is supported. + \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 CImg& draw_triangle(const int x0, const int y0, @@ -27861,15 +41809,14 @@ namespace cimg_library { 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).", + "draw_triangle(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; - static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, offx = _spectrum*whd-1; + static const T maxval = (T)std::min(cimg::type::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), @@ -27886,16 +41833,16 @@ namespace cimg_library { 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); + 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; + 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 = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); ptrd+=whd; } ptrd-=offx; @@ -27903,7 +41850,7 @@ namespace cimg_library { } 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); + const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whd; } @@ -27914,7 +41861,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d Gouraud-shaded colored triangle, with z-buffering. + //! Draw a Gouraud-shaded 2d triangle, with z-buffering \overloading. template CImg& draw_triangle(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -27926,20 +41873,20 @@ namespace cimg_library { const float brightness2, const float opacity=1) { typedef typename cimg::superset::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).", + "draw_triangle(): 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.", + "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_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, offx = _spectrum*whd; + static const T maxval = (T)std::min(cimg::type::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), @@ -27963,9 +41910,9 @@ namespace cimg_library { const int dx = xright - xleft, dc = cright>cleft?cright - cleft:cleft - cright, - rc = dx?(cright-cleft)/dx:0, + rc = dx?(cright - cleft)/dx:0, sc = cright>cleft?1:-1, - ndc = dc-(dx?dx*(dc/dx):0); + ndc = dc - (dx?dx*(dc/dx):0); const tzfloat pentez = (zright - zleft)/dx; int errc = dx>>1; if (xleft<0 && dx) { @@ -27973,15 +41920,15 @@ namespace cimg_library { zleft-=xleft*(zright - zleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + if (xright>=width() - 1) xright = width() - 1; T *ptrd = data(xleft,y); - tz *ptrz = zbuffer.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 = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); ptrd+=whd; } ptrd-=offx; @@ -27993,7 +41940,7 @@ namespace cimg_library { *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); + const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256); *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whd; } @@ -28007,7 +41954,19 @@ namespace cimg_library { return *this; } - //! Draw a colored triangle with interpolated colors. + //! 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 CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, @@ -28017,28 +41976,28 @@ namespace cimg_library { 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); + 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 2d textured triangle. + //! Draw a textured 2d triangle. /** - \param x0 = X-coordinate of the first corner in the image instance. - \param y0 = Y-coordinate of the first corner in the image instance. - \param x1 = X-coordinate of the second corner in the image instance. - \param y1 = Y-coordinate of the second corner in the image instance. - \param x2 = X-coordinate of the third corner in the image instance. - \param y2 = Y-coordinate of the third corner in the image instance. - \param texture = texture image used to fill the triangle. - \param tx0 = X-coordinate of the first corner in the texture image. - \param ty0 = Y-coordinate of the first corner in the texture image. - \param tx1 = X-coordinate of the second corner in the texture image. - \param ty1 = Y-coordinate of the second corner in the texture image. - \param tx2 = X-coordinate of the third corner in the texture image. - \param ty2 = Y-coordinate of the third corner in the texture image. - \param opacity = opacity of the drawing. - \param brightness = brightness of the drawing (in [0,2]). - \note Clipping is supported, but texture coordinates do not support clipping. + \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 CImg& draw_triangle(const int x0, const int y0, @@ -28050,19 +42009,22 @@ namespace cimg_library { 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).", + "draw_triangle(): 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_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); - static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + 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::max(),cimg::type::max()); const float - nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), - nbrightness = brightness<0?0:(brightness>2?2:brightness); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1; + nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f), + 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); @@ -28092,32 +42054,32 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data(txleft,tyleft); + const tc *col = &texture._atXY(txleft,tyleft); cimg_forC(*this,c) { *ptrd = (T)*col; - ptrd+=whd; col+=twhd; + 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.data(txleft,tyleft); + const tc *col = &texture._atXY(txleft,tyleft); cimg_forC(*this,c) { *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twhd; + 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.data(txleft,tyleft); + const tc *col = &texture._atXY(txleft,tyleft); cimg_forC(*this,c) { - *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); - ptrd+=whd; col+=twhd; + *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); + ptrd+=whd; col+=twh; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); @@ -28125,29 +42087,29 @@ namespace cimg_library { } } else { if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { - const tc *col = texture.data(txleft,tyleft); + const tc *col = &texture._atXY(txleft,tyleft); cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twhd; + 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.data(txleft,tyleft); + const tc *col = &texture._atXY(txleft,tyleft); cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); - ptrd+=whd; col+=twhd; + 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.data(txleft,tyleft); + const tc *col = &texture._atXY(txleft,tyleft); cimg_forC(*this,c) { - const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); @@ -28169,19 +42131,22 @@ namespace cimg_library { 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).", + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", cimg_instance, texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - 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)cimg::min(cimg::type::max(),cimg::type::max()); + 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::max(),(T)cimg::type::max()); const float - nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), - nbrightness = brightness<0?0:(brightness>2?2:brightness); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1; + nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f), + 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, @@ -28206,8 +42171,10 @@ namespace cimg_library { 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))); + 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; @@ -28227,58 +42194,58 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); cimg_forC(*this,c) { *ptrd = (T)*col; - ptrd+=whd; col+=twhd; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); cimg_forC(*this,c) { *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twhd; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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+=twhd; + *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.data((int)(txleft*invz),(int)(tyleft*invz)); + const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twhd; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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+=twhd; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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); + const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } @@ -28288,7 +42255,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d textured triangle, with z-buffering and perspective correction. + //! Draw a textured 2d triangle, with perspective correction and z-buffering. template CImg& draw_triangle(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -28301,25 +42268,29 @@ namespace cimg_library { const float opacity=1, const float brightness=1) { typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=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.", + "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).", + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", cimg_instance, texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - 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)cimg::min(cimg::type::max(),cimg::type::max()); + 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::max(),(T)cimg::type::max()); const float - nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), - nbrightness = brightness<0?0:(brightness>2?2:brightness); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd; + nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f), + 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, @@ -28339,8 +42310,10 @@ namespace cimg_library { 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))); + 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), @@ -28362,7 +42335,7 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + if (xright>=width() - 1) xright = width() - 1; T *ptrd = data(xleft,y,0,0); tz *ptrz = zbuffer.data(xleft,y); if (opacity>=1) { @@ -28370,10 +42343,10 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); cimg_forC(*this,c) { *ptrd = (T)*col; - ptrd+=whd; col+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -28382,10 +42355,10 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); cimg_forC(*this,c) { *ptrd = (T)(nbrightness**col); - ptrd+=whd; col+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -28394,10 +42367,10 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + 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+=twhd; + *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -28408,10 +42381,10 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz)); cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); - ptrd+=whd; col+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -28420,10 +42393,10 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + 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+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -28432,11 +42405,11 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + 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); + const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; col+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -28448,24 +42421,23 @@ namespace cimg_library { return *this; } - //! Draw a 2d Pseudo-Phong-shaded triangle. + //! Draw a Phong-shaded 2d triangle. /** - \param x0 = X-coordinate of the first corner in the image instance. - \param y0 = Y-coordinate of the first corner in the image instance. - \param x1 = X-coordinate of the second corner in the image instance. - \param y1 = Y-coordinate of the second corner in the image instance. - \param x2 = X-coordinate of the third corner in the image instance. - \param y2 = Y-coordinate of the third corner in the image instance. - \param color = array of spectrum() values of type \c T, defining the global drawing color. - \param light = light image. - \param lx0 = X-coordinate of the first corner in the light image. - \param ly0 = Y-coordinate of the first corner in the light image. - \param lx1 = X-coordinate of the second corner in the light image. - \param ly1 = Y-coordinate of the second corner in the light image. - \param lx2 = X-coordinate of the third corner in the light image. - \param ly2 = Y-coordinate of the third corner in the light image. - \param opacity = opacity of the drawing. - \note Clipping is supported, but texture coordinates do not support clipping. + \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 CImg& draw_triangle(const int x0, const int y0, @@ -28477,22 +42449,24 @@ namespace cimg_library { 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).", + "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).", + "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_empty()) return *this; 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)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + static const T maxval = (T)std::min(cimg::type::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); 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 int whd = _width*_height*_depth, offx = _spectrum*whd-1; + 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); @@ -28520,25 +42494,27 @@ namespace cimg_library { lyleft-=xleft*(lyright - lyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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 = light(lxleft,lyleft,c); - *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); - ptrd+=whd; + 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 = light(lxleft,lyleft,c); - const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); + 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; + ptrd+=whd; lig+=lwh; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); @@ -28548,7 +42524,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d Pseudo-Phong-shaded triangle, with z-buffering. + //! Draw a Phong-shaded 2d triangle, with z-buffering. template CImg& draw_triangle(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -28561,26 +42537,29 @@ namespace cimg_library { const int lx2, const int ly2, const float opacity=1) { typedef typename cimg::superset::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).", + "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).", + "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_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.", + "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_empty() || z0<=0 || z1<=0 || z2<=0) return *this; 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)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, offx = _spectrum*whd; + static const T maxval = (T)std::min(cimg::type::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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; @@ -28621,18 +42600,19 @@ namespace cimg_library { lyleft-=xleft*(lyright - lyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + if (xright>=width() - 1) xright = width() - 1; T *ptrd = data(xleft,y,0,0); - tz *ptrz = zbuffer.data(xleft,y); + 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 = light(lxleft,lyleft,c); + const tl l = *lig; const tc cval = *(col++); - *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval); - ptrd+=whd; + *ptrd = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval); + ptrd+=whd; lig+=lwh; } ptrd-=offx; } @@ -28643,12 +42623,13 @@ namespace cimg_library { 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 = light(lxleft,lyleft,c); + const tl l = *lig; const tc cval = *(col++); - const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval); + const T val = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval); *ptrd = (T)(nopacity*val + *ptrd*copacity); - ptrd+=whd; + ptrd+=whd; lig+=lwh; } ptrd-=offx; } @@ -28661,26 +42642,25 @@ namespace cimg_library { return *this; } - //! Draw a 2d Gouraud-shaded textured triangle. + //! Draw a textured Gouraud-shaded 2d triangle. /** - \param x0 = X-coordinate of the first corner in the image instance. - \param y0 = Y-coordinate of the first corner in the image instance. - \param x1 = X-coordinate of the second corner in the image instance. - \param y1 = Y-coordinate of the second corner in the image instance. - \param x2 = X-coordinate of the third corner in the image instance. - \param y2 = Y-coordinate of the third corner in the image instance. - \param texture = texture image used to fill the triangle. - \param tx0 = X-coordinate of the first corner in the texture image. - \param ty0 = Y-coordinate of the first corner in the texture image. - \param tx1 = X-coordinate of the second corner in the texture image. - \param ty1 = Y-coordinate of the second corner in the texture image. - \param tx2 = X-coordinate of the third corner in the texture image. - \param ty2 = Y-coordinate of the third corner in the texture image. - \param brightness0 = brightness value of the first corner. - \param brightness1 = brightness value of the second corner. - \param brightness2 = brightness value of the third corner. - \param opacity = opacity of the drawing. - \note Clipping is supported, but texture coordinates do not support clipping. + \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 CImg& draw_triangle(const int x0, const int y0, @@ -28694,18 +42674,21 @@ namespace cimg_library { 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).", + "draw_triangle(): 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_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity); - static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1; + 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::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), @@ -28744,24 +42727,24 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data(txleft,tyleft); + 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+=twhd; + *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.data(txleft,tyleft); + 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); + 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+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); @@ -28772,7 +42755,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d Gouraud-shaded textured triangle, with perspective correction. + //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction \overloading. template CImg& draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, @@ -28785,18 +42768,20 @@ namespace cimg_library { 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).", + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", cimg_instance, texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; 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)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1; + static const T maxval = (T)std::min(cimg::type::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), @@ -28824,8 +42809,10 @@ namespace cimg_library { 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))); + 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 @@ -28854,24 +42841,24 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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+=twhd; + *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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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); + 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+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); @@ -28881,7 +42868,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d Gouraud-shaded textured triangle, with z-buffering and perspective correction. + //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction and z-buffering \overloading. template CImg& draw_triangle(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -28896,24 +42883,27 @@ namespace cimg_library { const float brightness2, const float opacity=1) { typedef typename cimg::superset::type tzfloat; + if (is_empty() || z0<=0 || z1<=0 || z2<=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.", + "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).", + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", cimg_instance, texture._width,texture._height,texture._depth,texture._spectrum,texture._data); - - if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + 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)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd; + static const T maxval = (T)std::min(cimg::type::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f), nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f), @@ -28936,8 +42926,10 @@ namespace cimg_library { 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))); + 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), @@ -28966,17 +42958,17 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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+=twhd; + *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256); + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -28986,11 +42978,11 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + 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); + 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+=twhd; + ptrd+=whd; col+=twh; } ptrd-=offx; } @@ -29002,30 +42994,29 @@ namespace cimg_library { return *this; } - //! Draw a 2d Pseudo-Phong-shaded textured triangle. + //! Draw a textured Phong-shaded 2d triangle. /** - \param x0 = X-coordinate of the first corner in the image instance. - \param y0 = Y-coordinate of the first corner in the image instance. - \param x1 = X-coordinate of the second corner in the image instance. - \param y1 = Y-coordinate of the second corner in the image instance. - \param x2 = X-coordinate of the third corner in the image instance. - \param y2 = Y-coordinate of the third corner in the image instance. - \param texture = texture image used to fill the triangle. - \param tx0 = X-coordinate of the first corner in the texture image. - \param ty0 = Y-coordinate of the first corner in the texture image. - \param tx1 = X-coordinate of the second corner in the texture image. - \param ty1 = Y-coordinate of the second corner in the texture image. - \param tx2 = X-coordinate of the third corner in the texture image. - \param ty2 = Y-coordinate of the third corner in the texture image. - \param light = light image. - \param lx0 = X-coordinate of the first corner in the light image. - \param ly0 = Y-coordinate of the first corner in the light image. - \param lx1 = X-coordinate of the second corner in the light image. - \param ly1 = Y-coordinate of the second corner in the light image. - \param lx2 = X-coordinate of the third corner in the light image. - \param ly2 = Y-coordinate of the third corner in the light image. - \param opacity = opacity of the drawing. - \note Clipping is supported, but texture coordinates do not support clipping. + \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 CImg& draw_triangle(const int x0, const int y0, @@ -29040,22 +43031,27 @@ namespace cimg_library { 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).", + "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).", + "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_empty()) return *this; - 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)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1; + 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::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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; @@ -29063,6 +43059,9 @@ namespace cimg_library { 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 @@ -29098,14 +43097,16 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data(txleft,tyleft); + 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 = light(lxleft,lyleft,c); - *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); - ptrd+=whd; col+=twhd; + 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); @@ -29113,12 +43114,14 @@ namespace cimg_library { 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.data(txleft,tyleft); + 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 = light(lxleft,lyleft,c); - const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + 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+=twhd; + ptrd+=whd; col+=twh; lig+=lwh; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); @@ -29130,7 +43133,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d Pseudo-Phong-shaded textured triangle, with perspective correction. + //! Draw a textured Phong-shaded 2d triangle, with perspective correction. template CImg& draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, @@ -29144,22 +43147,29 @@ namespace cimg_library { 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).", + "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).", + "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_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - 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)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1; + 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::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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 @@ -29185,8 +43195,13 @@ namespace cimg_library { 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))); + 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; } @@ -29198,7 +43213,8 @@ namespace cimg_library { zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; - if (xrightlxleft?lxright - lxleft:lxleft - lxright, @@ -29222,27 +43238,31 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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 = light(lxleft,lyleft,c); - *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); - ptrd+=whd; col+=twhd; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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 = light(lxleft,lyleft,c); - const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + 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+=twhd; + ptrd+=whd; col+=twh; lig+=lwh; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); @@ -29253,7 +43273,7 @@ namespace cimg_library { return *this; } - //! Draw a 2d Pseudo-Phong-shaded textured triangle, with z-buffering and perspective correction. + //! Draw a textured Phong-shaded 2d triangle, with perspective correction and z-buffering. template CImg& draw_triangle(CImg& zbuffer, const int x0, const int y0, const float z0, @@ -29269,29 +43289,35 @@ namespace cimg_library { const int lx2, const int ly2, const float opacity=1) { typedef typename cimg::superset::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.", + "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).", + "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).", + "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_empty() || z0<=0 || z1<=0 || z2<=0) return *this; - 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)cimg::min(cimg::type::max(),cimg::type::max()); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd; + 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::max(),(T)cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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 @@ -29312,14 +43338,19 @@ namespace cimg_library { 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))); + 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; } @@ -29329,7 +43360,8 @@ namespace cimg_library { lyleft = lyleft0, lyright = lyright0; float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; tzfloat zleft = zl, zright = zr; - if (xrightlxleft?lxright - lxleft:lxleft - lxright, @@ -29351,18 +43383,20 @@ namespace cimg_library { tyleft-=xleft*(tyright - tyleft)/dx; } if (xleft<0) xleft = 0; - if (xright>=width()-1) xright = width()-1; + 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.data((int)(txleft*invz),(int)(tyleft*invz)); + 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 = light(lxleft,lyleft,c); - *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); - ptrd+=whd; col+=twhd; + const tl l = *lig; + *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval); + ptrd+=whd; col+=twh; lig+=lwh; } ptrd-=offx; } @@ -29373,12 +43407,14 @@ namespace cimg_library { if (zleft>=(tzfloat)*ptrz) { *ptrz = (tz)zleft; const tzfloat invz = 1/zleft; - const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz)); + 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 = light(lxleft,lyleft,c); - const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + 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+=twhd; + ptrd+=whd; col+=twh; lig+=lwh; } ptrd-=offx; } @@ -29391,7 +43427,7 @@ namespace cimg_library { return *this; } - //! Draw a 4d filled rectangle in the image instance, at coordinates (\c x0,\c y0,\c z0,\c c0)-(\c x1,\c y1,\c z1,\c c1). + //! 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. @@ -29402,27 +43438,27 @@ namespace cimg_library { \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 (optional). - \note - - Clipping is supported. + \param opacity Drawing opacity. **/ CImg& 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 bool bx = (x0=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 unsigned int offX = _width - lX, offY = _width*(_height - lY), offZ = _width*_height*(_depth - lZ); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,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.0f); 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 CImg& 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).", + "draw_rectangle(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; - cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,color[c],opacity); + cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity); return *this; } - //! Draw a 3d outlined colored rectangle in the image instance. + //! Draw an outlined 3d rectangle \overloading. template CImg& draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, @@ -29487,25 +43520,23 @@ namespace cimg_library { draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); } - //! Draw a 2d filled colored rectangle in the image instance, at coordinates (\c x0,\c y0)-(\c x1,\c y1). + //! 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 (optional). - \note - - Clipping is supported. + \param opacity Drawing opacity. **/ template CImg& 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); + return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity); } - //! Draw a 2d outlined colored rectangle. + //! Draw a outlined 2d rectangle \overloading. template CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, @@ -29514,92 +43545,97 @@ namespace cimg_library { 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 bool bx = (x0 - CImg& draw_polygon(const CImg& points, + //! 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 + CImg& draw_polygon(const CImg& 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).", + "draw_polygon(): Specified color is (null).", cimg_instance); - - if (is_empty() || !points || points._width<3) return *this; - - // Normalize 2d input coordinates. - CImg 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=width() || ymax<0 || ymin>=height()) return *this; - if (ymin==ymax) return _draw_scanline(xmin,xmax,ymin,color,opacity); - const unsigned int - nymin = ymin<0?0:(unsigned int)ymin, - nymax = ymax>=height()?_height-1:(unsigned int)ymax, - dy = 1 + nymax - nymin; - CImg X(1+2*nb_points,dy,1,1,0), tmp; - int cx = (int)npoints(0,0), cy = (int)npoints(0,1); - unsigned int cp = 0; - for (unsigned int p = 0; p Xs(points._width,ymax - ymin + 1); + CImg 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 - nx = (int)npoints(np,0), ny = (int)npoints(np,1), ay = (int)npoints(ap,1), - y0 = cy - nymin, y1 = ny - nymin; + 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 countermin = ((nyay && cy>ny))?1:0; - for (int x = cx, y = y0, _sx = 1, _sy = 1, - _dx = nx>cx?nx-cx:((_sx=-1),cx-nx), - _dy = y1>y0?y1-y0:((_sy=-1),y0-y1), - _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy), - _err = _dx>>1, - _rx = _dy?(nx-cx)/_dy:0; - _counter>=countermin; - --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0)) - if (y>=0 && y<(int)dy) X(++X(0,y),y) = x; - cp = np; cx = nx; cy = ny; - } else { - const int pp = (cp?cp-1:nb_points-1), py = (int)npoints(pp,1); - if (y0>=0 && y0<(int)dy && (!p || (cy>py && ay>cy) || (cyn; + n = nn; + nn = an; } - // Draw polygon scanlines. - for (int y = 0; y<(int)dy; ++y) { - tmp.assign(X.data(1,y),X(0,y),1,1,1,true).sort(); - for (int i = 1; i<=X(0,y); ) { - const int xb = X(i++,y), xe = X(i++,y); - _draw_scanline(xb,xe,nymin+y,color,opacity); + cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>32)) + cimg_forY(Xs,y) { + const CImg Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort(); + int px = width(); + for (unsigned int n = 0; n CImg& draw_polygon(const CImg& points, const tc *const color, const float opacity, const unsigned int pattern) { @@ -29608,7 +43644,7 @@ namespace cimg_library { switch (points._height) { case 0 : case 1 : throw CImgArgumentException(_cimg_instance - "draw_polygon() : Invalid specified point set.", + "draw_polygon(): Invalid specified point set.", cimg_instance); case 2 : { // 2d version. CImg npoints(points._width,2); @@ -29630,11 +43666,17 @@ namespace cimg_library { } break; default : { // 3d version. CImg 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); + 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 - CImg& draw_circle(const int x0, const int y0, int radius, - const tc *const color, const float opacity=1) { - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_circle : Specified color is (null).", - cimg_instance); - - if (is_empty()) return *this; - _draw_scanline(color,opacity); - if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this; - if (y0>=0 && y0=0) { - const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y; - if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 - CImg& draw_circle(const int x0, const int y0, int radius, - const tc *const color, const float opacity, - const unsigned int) { - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_circle : Specified color is (null).", - cimg_instance); - - if (is_empty()) return *this; - 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=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 a 2d ellipse (inner routine). - template - CImg& _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 (!color) - throw CImgArgumentException(_cimg_instance - "draw_ellipse : Specified color is (null).", - cimg_instance); - if (is_empty()) return *this; - if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity); - _draw_scanline(color,opacity); - const float - nr1 = cimg::abs(r1), nr2 = cimg::abs(r2), - nangle = (float)(angle*cimg::PI/180), - u = (float)std::cos(nangle), - v = (float)std::sin(nangle), - rmax = cimg::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)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 + (y0?(float)std::sqrt(delta)/a:0.0f, - bY = b*Y/a, - fxmin = x0 - 0.5f - bY - sdelta, - fxmax = x0 + 0.5f - bY + sdelta; - const int xmin = (int)fxmin, xmax = (int)fxmax; - if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity); - else { - if (first_line) { - if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity); - else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity); - first_line = false; - } else { - if (xmin CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, @@ -29803,13 +43708,13 @@ namespace cimg_library { return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U); } - //! Draw a filled ellipse. + //! 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 = array of spectrum() values of type \c T, defining the drawing color. - \param opacity = opacity of the drawing. + \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 CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, @@ -29821,17 +43726,16 @@ namespace cimg_library { color,opacity); } - //! Draw an outlined ellipse. + //! 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 ru = X-coordinate of the orientation vector related to the first radius. - \param rv = Y-coordinate of the orientation vector related to the first radius. - \param color = array of spectrum() values of type \c T, defining the drawing color. - \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. - \param opacity = opacity of the drawing. + \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 CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, @@ -29840,14 +43744,14 @@ namespace cimg_library { return *this; } - //! Draw an outlined ellipse. + //! 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 = array of spectrum() values of type \c T, defining the drawing color. - - \param opacity = opacity of the drawing. + \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 CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, @@ -29860,6 +43764,147 @@ namespace cimg_library { color,opacity,pattern); } + template + CImg& _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,r1,color,opacity,pattern); + else return draw_circle(x0,y0,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)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 + (y0?(float)std::sqrt(delta)/a:0.0f, + 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 + CImg& 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=0) { + const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y; + if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 + CImg& 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=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. @@ -29867,20 +43912,15 @@ namespace cimg_library { \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 (optional). - \note - - Clipping is supported. + \param opacity Drawing opacity. **/ template CImg& draw_image(const int x0, const int y0, const int z0, const int c0, const CImg& sprite, const float opacity=1) { - if (!sprite) - throw CImgArgumentException(_cimg_instance - "draw_image() : Empty specified sprite.", - cimg_instance); - if (is_empty()) return *this; + 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) return assign(sprite,false); + 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), @@ -29888,16 +43928,19 @@ namespace cimg_library { 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*sprite.width():0) - - (bz?z0*sprite.width()*sprite.height():0) - - (bc?c0*sprite.width()*sprite.height()*sprite.depth():0); - const unsigned int - offX = _width - lX, soffX = sprite._width - lX, - offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY), - offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + *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.0f); 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& draw_image(const int x0, const int y0, const int z0, const int c0, const CImg& sprite, const float opacity=1) { - if (!sprite) - throw CImgArgumentException(_cimg_instance - "draw_image() : Empty specified sprite.", - cimg_instance); - - if (is_empty()) return *this; + 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) return assign(sprite,false); + 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), @@ -29933,26 +43972,30 @@ namespace cimg_library { 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*sprite.width():0) - - (bz?z0*sprite.width()*sprite.height():0) - - (bc?c0*sprite.width()*sprite.height()*sprite.depth():0); - const unsigned int - offX = _width - lX, soffX = sprite._width - lX, - offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY), - offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ), + *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 - cimg::max(opacity,0); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); 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=1) for (int y = 0; y=1) + for (int y = 0; y CImg& draw_image(const int x0, const int y0, const int z0, const CImg& sprite, const float opacity=1) { return draw_image(x0,y0,z0,0,sprite,opacity); } - //! Draw an image. + //! Draw an image \overloading. template CImg& draw_image(const int x0, const int y0, const CImg& sprite, const float opacity=1) { return draw_image(x0,y0,0,sprite,opacity); } - //! Draw an image. + //! Draw an image \overloading. template CImg& draw_image(const int x0, const CImg& sprite, const float opacity=1) { return draw_image(x0,0,sprite,opacity); } - //! Draw an image. + //! Draw an image \overloading. template CImg& draw_image(const CImg& sprite, const float opacity=1) { return draw_image(0,sprite,opacity); } - //! Draw a sprite image in the image instance (masked version). + //! Draw a masked image. /** \param sprite Sprite image. \param mask Mask image. @@ -29996,32 +44039,23 @@ namespace cimg_library { \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_valmax Maximum pixel value of the mask image \c mask (optional). + \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. - - Clipping is supported. - Dimensions along x,y and z of \p sprite and \p mask must be the same. **/ template CImg& draw_image(const int x0, const int y0, const int z0, const int c0, const CImg& sprite, const CImg& mask, const float opacity=1, - const float mask_valmax=1) { - if (!sprite) - throw CImgArgumentException(_cimg_instance - "draw_image() : Empty specified sprite.", - cimg_instance); - if (!mask) - throw CImgArgumentException(_cimg_instance - "draw_image() : Empty specified mask.", - cimg_instance); - - if (is_empty()) return *this; - if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_valmax); - if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_valmax); + 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.", + "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); @@ -30032,15 +44066,21 @@ namespace cimg_library { 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 int - coff = -(bx?x0:0)-(by?y0*mask.width():0)-(bz?z0*mask.width()*mask.height():0)-(bc?c0*mask.width()*mask.height()*mask.depth():0), - ssize = mask.width()*mask.height()*mask.depth(); + 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 unsigned int - offX = _width - lX, soffX = sprite._width - lX, - offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY), - offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ); + 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 CImg& draw_image(const int x0, const int y0, const int z0, const CImg& sprite, const CImg& mask, const float opacity=1, - const float mask_valmax=1) { - return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_valmax); + const float mask_max_value=1) { + return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value); } - //! Draw an image. + //! Draw a image \overloading. template CImg& draw_image(const int x0, const int y0, const CImg& sprite, const CImg& mask, const float opacity=1, - const float mask_valmax=1) { - return draw_image(x0,y0,0,sprite,mask,opacity,mask_valmax); + const float mask_max_value=1) { + return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value); } - //! Draw an image. + //! Draw a image \overloading. template CImg& draw_image(const int x0, const CImg& sprite, const CImg& mask, const float opacity=1, - const float mask_valmax=1) { - return draw_image(x0,0,sprite,mask,opacity,mask_valmax); + const float mask_max_value=1) { + return draw_image(x0,0,sprite,mask,opacity,mask_max_value); } //! Draw an image. template CImg& draw_image(const CImg& sprite, const CImg& mask, const float opacity=1, - const float mask_valmax=1) { - return draw_image(0,sprite,mask,opacity,mask_valmax); + const float mask_max_value=1) { + return draw_image(0,sprite,mask,opacity,mask_max_value); } - //! Draw a text. + //! 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 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 font Font used for drawing text. + \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 format 'printf'-style format string, followed by arguments. - \note Clipping is supported. + \param font Font used for drawing text. **/ template CImg& draw_text(const int x0, const int y0, @@ -30111,43 +44150,55 @@ namespace cimg_library { const tc1 *const foreground_color, const tc2 *const background_color, const float opacity, const CImgList& font, ...) { if (!font) return *this; - char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); + CImg 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 CImg& draw_text(const int x0, const int y0, const char *const text, const tc *const foreground_color, const int, const float opacity, const CImgList& font, ...) { if (!font) return *this; - char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font); + CImg 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 CImg& draw_text(const int x0, const int y0, const char *const text, const int, const tc *const background_color, const float opacity, const CImgList& font, ...) { if (!font) return *this; - char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font); + CImg 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. + //! 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 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 font_size Size of the font (exact match for 13,24,32,57). + \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 format 'printf'-style format string, followed by arguments. - \note Clipping is supported. + \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise). **/ template CImg& draw_text(const int x0, const int y0, @@ -30155,77 +44206,61 @@ namespace cimg_library { 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; - char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); - static CImgList font; - const unsigned int - ref_height = font_height<=13?13:font_height<=28?24:font_height<=32?32:57, - padding_x = font_height<=18?1:font_height<=32?2:3; - if (!font || font[0]._height!=font_height) { - font = CImgList::font(ref_height,true); - font[0].assign(1,font_height); - if (ref_height==font_height) cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,-100,-100,0); - } - if (is_empty()) { - if (font[0]._spectrum!=1) cimglist_for_in(font,0,255,l) font[l].channel(0); - } else if (font[0]._spectrum<_spectrum) cimglist_for_in(font,0,255,l) font[l].resize(-100,-100,1,_spectrum); - if (ref_height!=font_height) for (const char *ptrs = tmp; *ptrs; ++ptrs) { - const unsigned int __c = (unsigned int)(unsigned char)*ptrs, _c = (__c=='\t')?' ':__c; - if (_c &c = font[_c]; - if (c._height!=font_height) { - c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3); - c.resize(c._width + padding_x,-100,-100,-100,0); - } - } - if (_c+256U &c = font[_c+256]; - if (c._height!=font_height) { - c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3); - c.resize(c._width + padding_x,-100,-100,-100,0); - } - } - } - return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + const CImgList& font = CImgList::font(font_height,true); + _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true); + return *this; } + //! Draw a text string \overloading. template CImg& 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; - char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); - return draw_text(x0,y0,"%s",foreground_color,(const tc*)background_color,opacity,font_height,tmp); + cimg::unused(background_color); + CImg 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 CImg& 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; - char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap); - return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp); + CImg 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); } - // Draw a text (internal routine). template CImg& _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& font) { + const float opacity, const CImgList& font, + const bool is_native_font) { if (!text) return *this; if (!font) throw CImgArgumentException(_cimg_instance - "draw_text() : Empty specified font.", + "draw_text(): Empty specified font.", cimg_instance); - const unsigned int text_length = std::strlen(text); - if (is_empty()) { + + const unsigned int text_length = (unsigned int)std::strlen(text); + const bool _is_empty = is_empty(); + 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; iw) w = x; x = 0; break; case '\t' : x+=4*font[' ']._width; break; @@ -30236,43 +44271,46 @@ namespace cimg_library { if (x>w) w=x; y+=font[0]._height; } - assign(x0+w,y0+y,1,font[0]._spectrum,0); - if (background_color) cimg_forC(*this,c) get_shared_channel(c).fill((T)background_color[c]); + assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0); } int x = x0, y = y0; - CImg letter; for (unsigned int i = 0; i& mask = (c+256)<(int)font._width?font[c+256]:font[c]; - if (foreground_color) - for (unsigned int p = 0; p=512) draw_image(x,y,letter,mask,opacity,(T)1); - else draw_image(x,y,letter,opacity); - x+=letter._width; + CImg 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& draw_quiver(const CImg& flow, const t2 *const color, const float opacity=1, const unsigned int sampling=25, const float factor=-20, - const bool arrows=true, const unsigned int pattern=~0U) { - return draw_quiver(flow,CImg(color,_spectrum,1,1,1,true),opacity,sampling,factor,arrows,pattern); + const bool is_arrow=true, const unsigned int pattern=~0U) { + return draw_quiver(flow,CImg(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern); } - //! Draw a vector field in the image instance, using a colormap. + //! 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 opacity Opacity of the drawing. + \param is_arrow Tells if arrows must be drawn, instead of oriented segments. \param pattern Used pattern to draw lines. \note Clipping is supported. **/ @@ -30298,29 +44337,26 @@ namespace cimg_library { CImg& draw_quiver(const CImg& flow, const CImg& color, const float opacity=1, const unsigned int sampling=25, const float factor=-20, - const bool arrows=true, const unsigned int pattern=~0U) { - + 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).", + "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 " + "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,arrows,pattern); - + 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)cimg::max(cimg::abs(m),cimg::abs(M)); + vmax = (float)std::max(cimg::abs(m),cimg::abs(M)); if (!vmax) vmax = 1; fact = -factor; } else { fact = factor; vmax = 1; } @@ -30329,192 +44365,254 @@ namespace cimg_library { 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 (arrows) { - const int xx = x+(int)u, yy = y+(int)v; + 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.0f,pattern); else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,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); + 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 on the image instance. + //! Draw a labeled horizontal axis. /** - \param xvalues Lower bound of the x-range. + \param values_x Values along the horizontal axis. \param y Y-coordinate of the horizontal axis in the image instance. - \param color Array of spectrum() values of type \c T, defining the drawing color. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. \param opacity Drawing opacity. \param pattern Drawing pattern. - \param opacity_out Drawing opacity of 'outside' axes. - \note if \c precision==0, precision of the labels is automatically computed. + \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 - CImg& draw_axis(const CImg& xvalues, const int y, + CImg& draw_axis(const CImg& values_x, const int y, const tc *const color, const float opacity=1, - const unsigned int pattern=~0U) { - if (!is_empty()) { - int siz = (int)xvalues.size()-1; - if (siz<=0) draw_line(0,y,_width-1,y,color,opacity,pattern); - else { - if (xvalues[0] txt(32); + CImg 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]=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 on the image instance. + //! 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 - CImg& draw_axis(const int x, const CImg& yvalues, + CImg& draw_axis(const int x, const CImg& values_y, const tc *const color, const float opacity=1, - const unsigned int pattern=~0U) { - if (!is_empty()) { - int siz = (int)yvalues.size()-1; - if (siz<=0) draw_line(x,0,x,_height-1,color,opacity,pattern); - else { - if (yvalues[0]=height()-11?height()-11:tmp), - xt = x - (int)std::strlen(txt)*7; - draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity); - if (xt>0) draw_text(xt,nyi,txt,color,(tc*)0,opacity,13); - else draw_text(x+3,nyi,txt,color,(tc*)0,opacity,13); - } + 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 txt(32); + CImg 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]=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 a labeled horizontal+vertical axis on the image instance. + //! 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 - CImg& draw_axes(const CImg& xvalues, const CImg& yvalues, - const tc *const color, const float opacity=1, - const unsigned int patternx=~0U, const unsigned int patterny=~0U) { - if (!is_empty()) { - const CImg nxvalues(xvalues._data,xvalues.size(),1,1,1,true); - const int sizx = (int)xvalues.size()-1, wm1 = width()-1; - if (sizx>0) { - float ox = (float)nxvalues[0]; - for (unsigned int x = 1; x<_width; ++x) { - const float nx = (float)nxvalues._linear_atX((float)x*sizx/wm1); - if (nx*ox<=0) { draw_axis(nx==0?x:x-1,yvalues,color,opacity,patterny); break; } - ox = nx; - } + CImg& draw_axes(const CImg& values_x, const CImg& 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 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 nyvalues(yvalues._data,yvalues.size(),1,1,1,true); - const int sizy = (int)yvalues.size()-1, hm1 = height()-1; - if (sizy>0) { - float oy = (float)nyvalues[0]; - for (unsigned int y = 1; y<_height; ++y) { - const float ny = (float)nyvalues._linear_atX((float)y*sizy/hm1); - if (ny*oy<=0) { draw_axis(xvalues,ny==0?y:y-1,color,opacity,patternx); break; } - oy = ny; - } + } + const CImg 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 a labeled horizontal+vertical axis on the image instance. + //! Draw labeled horizontal and vertical axes \overloading. template CImg& 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 patternx=~0U, const unsigned int patterny=~0U) { - if (!is_empty()) { - const float - dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0), - px = (precisionx==0)?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx, - py = (precisiony==0)?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony; - if (x0!=x1 && y0!=y1) - draw_axes(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px), - CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py), - color,opacity,patternx,patterny); - else if (x0==x1 && y0!=y1) - draw_axis((int)x0,CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py), - color,opacity,patterny); - else if (x0!=x1 && y0==y1) - draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0, - color,opacity,patternx); - } + 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.0,(int)std::log10(dx) - 2.0):precisionx, + py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy) - 2.0):precisiony; + if (x0!=x1 && y0!=y1) + draw_axes(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px), + CImg::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::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::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0, + color,opacity,pattern_x,font_height); return *this; } - //! Draw grid. + //! 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 - CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, + CImg& draw_grid(const CImg& values_x, const CImg& values_y, const tc *const color, const float opacity=1, - const unsigned int patternx=~0U, const unsigned int patterny=~0U) { - if (!is_empty()) { - if (xvalues) cimg_foroff(xvalues,x) { - const int xi = (int)xvalues[x]; - if (xi>=0 && xi=0 && xi=0 && yi=0 && yi - CImg& draw_grid(const float deltax, const float deltay, + CImg& 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 patternx=~0U, const unsigned int patterny=~0U) { + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; CImg seqx, seqy; - if (deltax!=0) { - const float dx = deltax>0?deltax:_width*-deltax/100; + 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::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); + seqx = CImg::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 (deltay!=0) { - const float dy = deltay>0?deltay:_height*-deltay/100; + 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::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); + seqy = CImg::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,patternx,patterny); + return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y); } - //! Draw a 1d graph on the image instance. + //! Draw 1d graph. /** \param data Image containing the graph values I = f(x). - \param color Array of spectrum() values of type \c T, defining the drawing color. + \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 : + \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 : + \param vertex_type Define the type of points: - 0 = No points. - 1 = Point. - 2 = Straight cross. @@ -30534,68 +44632,79 @@ namespace cimg_library { 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).", + "draw_graph(): Specified color is (null).", cimg_instance); - if (is_empty() || _height<=1) return *this; // Create shaded colors for displaying bar plots. CImg color1, color2; if (plot_type==3) { color1.assign(_spectrum); color2.assign(_spectrum); - cimg_forC(*this,c) { color1[c] = (tc)cimg::min((float)cimg::type::max(),color[c]*1.2f); color2[c] = (tc)(color[c]*0.4f); } + cimg_forC(*this,c) { + color1[c] = (tc)std::min((float)cimg::type::max(),(float)color[c]*1.2f); + color2[c] = (tc)(color[c]*0.4f); + } } // Compute min/max and normalization factors. - const unsigned int + const ulongT siz = data.size(), - _siz1 = siz - (plot_type!=3?1:0), - siz1 = _siz1?_siz1:1, - _width1 = _width - (plot_type!=3?1:0), + _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); + 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); - for (unsigned int off = 1; off ndata(data._data,siz,1,1,1,true); - int oY = (int)((data[0]-m)/ca); + 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); + 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 = (off+1)*_width/siz-1, - Y = (int)((data[off]-m)/ca); + 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; + oX = X + 1; } } break; default : break; // No edges @@ -30603,11 +44712,12 @@ namespace cimg_library { // 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 = off*_width1/siz1 + wb2, + X = (int)(off*fx + wb2), Y = (int)((data[off]-m)/ca); draw_point(X,Y,color,opacity); } @@ -30615,23 +44725,23 @@ namespace cimg_library { case 2 : { // Straight Cross cimg_foroff(data,off) { const int - X = off*_width1/siz1 + wb2, + 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); + 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 = off*_width1/siz1 + wb2, + 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); + 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 = off*_width1/siz1 + wb2, + X = (int)(off*fx + wb2), Y = (int)((data[off]-m)/ca); draw_circle(X,Y,3,color,opacity); } @@ -30639,7 +44749,7 @@ namespace cimg_library { case 5 : { // Outlined circle cimg_foroff(data,off) { const int - X = off*_width1/siz1 + wb2, + X = (int)(off*fx + wb2), Y = (int)((data[off]-m)/ca); draw_circle(X,Y,3,color,opacity,0U); } @@ -30647,20 +44757,20 @@ namespace cimg_library { case 6 : { // Square cimg_foroff(data,off) { const int - X = off*_width1/siz1 + wb2, + X = (int)(off*fx + wb2), Y = (int)((data[off]-m)/ca); - draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U); + draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U); } } break; case 7 : { // Diamond cimg_foroff(data,off) { const int - X = off*_width1/siz1 + wb2, + 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); + 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 @@ -30668,411 +44778,349 @@ namespace cimg_library { return *this; } - //! Draw a 3d filled region starting from a point (\c x,\c y,\ z) in the image instance. + bool _draw_fill(const int x, const int y, const int z, + const CImg& 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 x X-coordinate of the starting point of the region to fill. - \param y Y-coordinate of the starting point of the region to fill. - \param z Z-coordinate of the starting point of the region to fill. - \param color An array of spectrum() values of type \c T, defining the drawing color. - \param region Image that will contain the mask of the filled region mask, as an output. - \param sigma Tolerance concerning neighborhood values. + \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 high_connexity Tells if 8-connexity must be used (only for 2d images). - \return \p region is initialized with the binary mask of the filled region. + \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 - CImg& draw_fill(const int x, const int y, const int z, - const tc *const color, const float opacity, - CImg& region, const float sigma=0, - const bool high_connexity=false) { + CImg& draw_fill(const int x0, const int y0, const int z0, + const tc *const color, const float opacity, + CImg ®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) -#define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \ - res = true; \ - const T *reference_col = reference_color._data + _spectrum, *ptrs = data(x,y,z) + siz; \ - for (unsigned int i = _spectrum; res && i; --i) { ptrs-=whd; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \ - region(x,y,z) = (t)(res?1:noregion); \ -} + if (!containsXYZC(x0,y0,z0,0)) return *this; + const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.0f); + const float tolerance2 = cimg::sqr(tolerance); + const CImg ref = get_vector_at(x0,y0,z0); + CImg stack(256,1,1,3); + CImg _region(_width,_height,_depth,1,0); + unsigned int N = 0; + int x, y, z; -#define _cimg_draw_fill_set(x,y,z) { \ - const tc *col = color; \ - T *ptrd = data(x,y,z); \ - 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; } \ -} + _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; -#define _cimg_draw_fill_insert(x,y,z) { \ - if (posr1>=remaining._height) remaining.resize(3,remaining._height<<1,1,1,0); \ - unsigned int *ptrr = remaining.data(0,posr1); \ - *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \ -} - -#define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \ - const unsigned int tx = x, ty = y, tz = z; \ - _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \ -} - - if (!color) - throw CImgArgumentException(_cimg_instance - "draw_fill() : Specified color is (null).", - cimg_instance); - - region.assign(_width,_height,_depth,1,(t)0); - if (x>=0 && x=0 && y=0 && z1); - const CImg reference_color = get_vector_at(x,y,z); - CImg remaining(3,512,1,1,0); - remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z; - unsigned int posr0 = 0, posr1 = 1; - region(x,y,z) = (t)1; - const t noregion = ((t)1==(t)2)?(t)0:(t)(-1); - if (is_3d) do { // 3d version of the filling algorithm - const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++); - if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; } - bool cont, res; - unsigned int nxc = xc; - do { // X-backward - _cimg_draw_fill_set(nxc,yc,zc); - _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0); - _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,ycposr0); - else do { // 2d version of the filling algorithm - const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++); - if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; } - bool cont, res; - unsigned int nxc = xc; - do { // X-backward - _cimg_draw_fill_set(nxc,yc,0); - _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0); - _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc=0 && x=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 (yn1) { + 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=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 (xn0) is_yp = true; + } + } + if (yn=0 && _draw_fill_is_inside(xp,yn,z)) { + _draw_fill_push(xp,yn,z); if (step<0) is_yn = true; + } + if (xn0) 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 (xn0) 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=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); } + if (xn=0 && _draw_fill_is_inside(xp,y,zn)) { + _draw_fill_push(xp,y,zn); if (step<0) is_zn = true; + } + if (xn0) 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=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); } + if (xnposr0); - if (noregion) cimg_for(region,ptrd,t) if (*ptrd==noregion) *ptrd = (t)0; - } - return *this; - } - - //! Draw a 3d filled region starting from a point (\c x,\c y,\ z) in the image instance. - /** - \param x = X-coordinate of the starting point of the region to fill. - \param y = Y-coordinate of the starting point of the region to fill. - \param z = Z-coordinate of the starting point of the region to fill. - \param color = an array of spectrum() values of type \c T, defining the drawing color. - \param sigma = tolerance concerning neighborhood values. - \param opacity = opacity of the drawing. - **/ - template - CImg& draw_fill(const int x, const int y, const int z, - const tc *const color, const float opacity=1, - const float sigma=0, const bool high_connexity=false) { - CImg tmp; - return draw_fill(x,y,z,color,opacity,tmp,sigma,high_connexity); - } - - //! Draw a 2d filled region starting from a point (\c x,\c y) in the image instance. - /** - \param x = X-coordinate of the starting point of the region to fill. - \param y = Y-coordinate of the starting point of the region to fill. - \param color = an array of spectrum() values of type \c T, defining the drawing color. - \param sigma = tolerance concerning neighborhood values. - \param opacity = opacity of the drawing. - **/ - template - CImg& draw_fill(const int x, const int y, - const tc *const color, const float opacity=1, - const float sigma=0, const bool high_connexity=false) { - CImg tmp; - return draw_fill(x,y,0,color,opacity,tmp,sigma,high_connexity); - } - - //! Draw a plasma random texture. - /** - \param x0 = X-coordinate of the upper-left corner of the plasma. - \param y0 = Y-coordinate of the upper-left corner of the plasma. - \param x1 = X-coordinate of the lower-right corner of the plasma. - \param y1 = Y-coordinate of the lower-right corner of the plasma. - \param alpha = Alpha-parameter of the plasma. - \param beta = Beta-parameter of the plasma. - \param opacity = opacity of the drawing. - **/ - CImg& draw_plasma(const int x0, const int y0, const int x1, const int y1, - const float alpha=1, const float beta=1, - const float opacity=1) { - if (!is_empty()) { - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - int nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1; - if (nx1=width()) nx1 = _width-1; - if (ny0<0) ny0 = 0; - if (ny1>=height()) ny1 = _height-1; - const int xc = (nx0+nx1)/2, yc = (ny0+ny1)/2, dx = (xc-nx0), dy = (yc-ny0); - const Tfloat - dc = (Tfloat)(std::sqrt((float)(dx*dx+dy*dy))*alpha + beta), - vmin = (Tfloat)cimg::type::min(), - vmax = (Tfloat)cimg::type::max(); - Tfloat val = 0; - cimg_forC(*this,c) { - if (opacity>=1) { - const Tfloat - val0 = (Tfloat)((*this)(nx0,ny0,0,c)), val1 = (Tfloat)((*this)(nx1,ny0,0,c)), - val2 = (Tfloat)((*this)(nx0,ny1,0,c)), val3 = (Tfloat)((*this)(nx1,ny1,0,c)); - (*this)(xc,ny0,0,c) = (T)((val0+val1)/2); - (*this)(xc,ny1,0,c) = (T)((val2+val3)/2); - (*this)(nx0,yc,0,c) = (T)((val0+val2)/2); - (*this)(nx1,yc,0,c) = (T)((val1+val3)/2); - do { - val = (Tfloat)(0.25f*((Tfloat)((*this)(nx0,ny0,0,c)) + - (Tfloat)((*this)(nx1,ny0,0,c)) + - (Tfloat)((*this)(nx1,ny1,0,c)) + - (Tfloat)((*this)(nx0,ny1,0,c))) + - dc*cimg::grand()); - } while (valvmax); - (*this)(xc,yc,0,c) = (T)val; - } else { - const Tfloat - val0 = (Tfloat)((*this)(nx0,ny0,0,c)), val1 = (Tfloat)((*this)(nx1,ny0,0,c)), - val2 = (Tfloat)((*this)(nx0,ny1,0,c)), val3 = (Tfloat)((*this)(nx1,ny1,0,c)); - (*this)(xc,ny0,0,c) = (T)(((val0+val1)*nopacity + copacity*(*this)(xc,ny0,0,c))/2); - (*this)(xc,ny1,0,c) = (T)(((val2+val3)*nopacity + copacity*(*this)(xc,ny1,0,c))/2); - (*this)(nx0,yc,0,c) = (T)(((val0+val2)*nopacity + copacity*(*this)(nx0,yc,0,c))/2); - (*this)(nx1,yc,0,c) = (T)(((val1+val3)*nopacity + copacity*(*this)(nx1,yc,0,c))/2); - do { - val = (Tfloat)(0.25f*(((Tfloat)((*this)(nx0,ny0,0,c)) + - (Tfloat)((*this)(nx1,ny0,0,c)) + - (Tfloat)((*this)(nx1,ny1,0,c)) + - (Tfloat)((*this)(nx0,ny1,0,c))) + - dc*cimg::grand())*nopacity + copacity*(*this)(xc,yc,0,c)); - } while (valvmax); - (*this)(xc,yc,0,c) = (T)val; + 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; } + } } - if (xc!=nx0 || yc!=ny0) { - draw_plasma(nx0,ny0,xc,yc,alpha,beta,opacity); - draw_plasma(xc,ny0,nx1,yc,alpha,beta,opacity); - draw_plasma(nx0,yc,xc,ny1,alpha,beta,opacity); - draw_plasma(xc,yc,nx1,ny1,alpha,beta,opacity); + } + _region.move_to(region); + return *this; + } + + //! Draw filled 3d region with the flood fill algorithm \simplification. + template + CImg& 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 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 + CImg& 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 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& 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::min(), M = (Tfloat)cimg::type::max(); + cimg_forZC(*this,z,c) { + CImg ref = get_shared_slice(z,c); + for (int delta = 1<1; delta>>=1) { + const int delta2 = delta>>1; + const float r = alpha*delta + beta; + + // Square step. + for (int y0 = 0; y0M?M:val); + } + + // Diamond steps. + for (int y = -delta2; yM?M:val); + } + for (int y0 = 0; y0M?M:val); + } + for (int y = -delta2; yM?M:val); + } } } return *this; } - //! Draw a plasma random texture. + //! Draw a quadratic Mandelbrot or Julia 2d fractal. /** - \param alpha = Alpha-parameter of the plasma. - \param beta = Beta-parameter of the plasma. - \param opacity = opacity of the drawing. + \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. **/ - CImg& draw_plasma(const float alpha=1, const float beta=1, - const float opacity=1) { - return draw_plasma(0,0,_width-1,_height-1,alpha,beta,opacity); - } - - //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm. template CImg& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, - const CImg& color_palette, const float opacity=1, + const CImg& 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 normalized_iteration=false, - const bool julia_set=false, - const double paramr=0, const double parami=0) { + 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 palette; - if (color_palette) palette.assign(color_palette._data,color_palette.size()/color_palette._spectrum,1,1,color_palette._spectrum,true); + 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 color palette (%u,%u,%u,%u,%p) have incompatible dimensions.", + "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", cimg_instance, - color_palette._width,color_palette._height,color_palette._depth,color_palette._spectrum,color_palette._data); + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)std::log(2.0); - unsigned int iteration = 0; - cimg_for_inXY(*this,x0,y0,x1,y1,p,q) { - const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height; - double zr, zi, cr, ci; - if (julia_set) { zr = x; zi = y; cr = paramr; ci = parami; } - else { zr = paramr; zi = parami; 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); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f), ln2 = (float)std::log(2.0); + 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)>=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; } - } else if (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); + 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 (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); + 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 fractal set, computed using the Escape Time Algorithm. + //! Draw a quadratic Mandelbrot or Julia 2d fractal \overloading. template - CImg& draw_mandelbrot(const CImg& color_palette, const float opacity=1, + CImg& draw_mandelbrot(const CImg& 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 normalized_iteration=false, - const bool julia_set=false, - const double paramr=0, const double parami=0) { - return draw_mandelbrot(0,0,_width-1,_height-1,color_palette,opacity, - z0r,z0i,z1r,z1i,iteration_max,normalized_iteration,julia_set,paramr,parami); + 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 in the image instance. + //! Draw a 1d gaussian function. /** - \param xc = X-coordinate of the gaussian center. - \param sigma = Standard variation of the gaussian distribution. - \param color = array of spectrum() values of type \c T, defining the drawing color. - \param opacity = opacity of the drawing. + \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 CImg& 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).", + "draw_gaussian(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; - const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const unsigned int whd = _width*_height*_depth; + const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + 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); @@ -31084,33 +45132,32 @@ namespace cimg_library { return *this; } - //! Draw an anisotropic 2d gaussian function. + //! Draw a 2d gaussian function. /** - \param xc = X-coordinate of the gaussian center. - \param yc = Y-coordinate of the gaussian center. - \param tensor = 2x2 covariance matrix. - \param color = array of spectrum() values of type \c T, defining the drawing color. - \param opacity = opacity of the drawing. + \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 CImg& draw_gaussian(const float xc, const float yc, const CImg& 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.", + "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).", + "draw_gaussian(): Specified color is (null).", cimg_instance); - - if (is_empty()) return *this; typedef typename CImg::Tfloat tfloat; const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const unsigned int whd = _width*_height*_depth; + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f); + const ulongT whd = (ulongT)_width*_height*_depth; const tc *col = color; float dy = -yc; cimg_forY(*this,y) { @@ -31128,7 +45175,7 @@ namespace cimg_library { return *this; } - //! Draw an anisotropic 2d gaussian function. + //! Draw a 2d gaussian function \overloading. template CImg& 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) { @@ -31140,29 +45187,14 @@ namespace cimg_library { return draw_gaussian(xc,yc,tensor,color,opacity); } - //! Draw an isotropic 2d gaussian function. - /** - \param xc = X-coordinate of the gaussian center. - \param yc = Y-coordinate of the gaussian center. - \param sigma = standard variation of the gaussian distribution. - \param color = array of spectrum() values of type \c T, defining the drawing color. - \param opacity = opacity of the drawing. - **/ + //! Draw a 2d gaussian function \overloading. template CImg& 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::diagonal(sigma,sigma),color,opacity); } - //! Draw an anisotropic 3d gaussian function. - /** - \param xc = X-coordinate of the gaussian center. - \param yc = Y-coordinate of the gaussian center. - \param zc = Z-coordinate of the gaussian center. - \param tensor = 3x3 covariance matrix. - \param color = array of spectrum() values of type \c T, defining the drawing color. - \param opacity = opacity of the drawing. - **/ + //! Draw a 3d gaussian function \overloading. template CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, const tc *const color, const float opacity=1) { @@ -31170,14 +45202,14 @@ namespace cimg_library { typedef typename CImg::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.", + "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 invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); - const tfloat a = invT(0,0), b = 2*invT(1,0), c = 2*invT(2,0), d = invT(1,1), e = 2*invT(2,1), f = invT(2,2); - const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); - const unsigned int whd = _width*_height*_depth; + 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.0f); + const ulongT whd = (ulongT)_width*_height*_depth; const tc *col = color; cimg_forXYZ(*this,x,y,z) { const float @@ -31191,15 +45223,7 @@ namespace cimg_library { return *this; } - //! Draw an isotropic 3d gaussian function. - /** - \param xc = X-coordinate of the gaussian center. - \param yc = Y-coordinate of the gaussian center. - \param zc = Z-coordinate of the gaussian center. - \param sigma = standard variation of the gaussian distribution. - \param color = array of spectrum() values of type \c T, defining the drawing color. - \param opacity = opacity of the drawing. - **/ + //! Draw a 3d gaussian function \overloading. template CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, const tc *const color, const float opacity=1) { @@ -31208,44 +45232,48 @@ namespace cimg_library { //! Draw a 3d object. /** - \param X = X-coordinate of the 3d object position - \param Y = Y-coordinate of the 3d object position - \param Z = 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 = Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) - \param double_sided = Tell 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_shine = Shininess of the 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 **/ template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, - const bool double_sided=false, const float focale=500, + 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_light=0.2f, const float specular_shine=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz, - specular_light,specular_shine,CImg::empty()); + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); } + //! Draw a 3d object \simplification. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, - const float specular_light, const float specular_shine, + const float specular_lightness, const float specular_shininess, CImg& zbuffer) { return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1); + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,1); } #ifdef cimg_use_board @@ -31255,11 +45283,12 @@ namespace cimg_library { const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, - const bool double_sided=false, const float focale=500, + 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_light=0.2f, const float specular_shine=0.1f) { - return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz, - specular_light,specular_shine,CImg::empty()); + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); } template @@ -31268,38 +45297,43 @@ namespace cimg_library { const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, - const float specular_light, const float specular_shine, + const float specular_lightness, const float specular_shininess, CImg& zbuffer) { return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1); + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,1); } #endif + //! Draw a 3d object \simplification. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, - const bool double_sided=false, const float focale=500, + 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_light=0.2f, const float specular_shine=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz, - specular_light,specular_shine,CImg::empty()); + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); } + //! Draw a 3d object \simplification. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, - const float specular_light, const float specular_shine, + const float specular_lightness, const float specular_shininess, CImg& zbuffer) { return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1); + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,1); } #ifdef cimg_use_board @@ -31309,11 +45343,12 @@ namespace cimg_library { const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, - const bool double_sided=false, const float focale=500, + 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_light=0.2f, const float specular_shine=0.1f) { - return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,double_sided,focale,lightx,lighty,lightz, - specular_light,specular_shine,CImg::empty()); + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); } template @@ -31322,39 +45357,43 @@ namespace cimg_library { const CImg& vertices, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, - const float specular_light, const float specular_shine, + const float specular_lightness, const float specular_shininess, CImg& zbuffer) { return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,1); + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,1); } #endif - //! Draw a 3d object. + //! Draw a 3d object \simplification. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& vertices, const CImgList& primitives, const CImgList& colors, const unsigned int render_type=4, - const bool double_sided=false, const float focale=500, + 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_light=0.2f, const float specular_shine=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,CImg::empty()); + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); } + //! Draw a 3d object \simplification. template CImg& draw_object3d(const float x0, const float y0, const float z0, const CImg& vertices, const CImgList& primitives, const CImgList& colors, const unsigned int render_type, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, - const float specular_light, const float specular_shine, + const float specular_lightness, const float specular_shininess, CImg& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer); + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,zbuffer); } #ifdef cimg_use_board @@ -31364,11 +45403,12 @@ namespace cimg_library { const CImg& vertices, const CImgList& primitives, const CImgList& colors, const unsigned int render_type=4, - const bool double_sided=false, const float focale=500, + 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_light=0.2f, const float specular_shine=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,CImg::empty()); + const float specular_lightness=0.2f, const float specular_shininess=0.1f) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,CImg::empty()); } template @@ -31377,27 +45417,38 @@ namespace cimg_library { const CImg& vertices, const CImgList& primitives, const CImgList& colors, const unsigned int render_type, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, - const float specular_light, const float specular_shine, + const float specular_lightness, const float specular_shininess, CImg& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), - render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer); + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,zbuffer); } #endif - template - void __draw_object3d(const unsigned int n_primitive, const CImgList& opacities, const CImg& color, - const int nx0, const int ny0, const CImg& sprite, const float opac) { - if (n_primitive + static float __draw_object3d(const CImgList& opacities, const unsigned int n_primitive, CImg& 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.0f; } - template - void __draw_object3d(const unsigned int, const CImg&, const CImg&, - const int nx0, const int ny0, const CImg& sprite, const float opac) { - draw_image(nx0,ny0,sprite,opac); + template + static float __draw_object3d(const CImg& opacities, const unsigned int n_primitive, CImg& opacity) { + opacity.assign(); + return n_primitive>=opacities._width?1.0f:(float)opacities[n_primitive]; + } + + template + static float ___draw_object3d(const CImgList& opacities, const unsigned int n_primitive) { + return n_primitive + static float ___draw_object3d(const CImg& opacities, const unsigned int n_primitive) { + return n_primitive @@ -31408,23 +45459,26 @@ namespace cimg_library { const CImgList& colors, const to& opacities, const unsigned int render_type, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float lightx, const float lighty, const float lightz, - const float specular_light, const float specular_shine, + const float specular_lightness, const float specular_shininess, const float sprite_scale) { typedef typename cimg::superset2::type tpfloat; + typedef typename to::value_type _to; if (is_empty() || !vertices || !primitives) return *this; - char error_message[1024] = { 0 }; + CImg 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); + "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_light<0.0f?0.0f:(specular_light>1.0f?1.0f:specular_light)), - nspec2 = 1 + (specular_shine<0.0f?0.0f:specular_shine), + nspec = 1 - (specular_lightness<0.0f?0.0f:(specular_lightness>1.0f?1.0f:specular_lightness)), + nspec2 = 1 + (specular_shininess<0.0f?0.0f:specular_shininess), nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1), nsl2 = 1 - 2*nsl1*nspec, nsl3 = nspec2 - nsl1 - nsl2; @@ -31441,37 +45495,39 @@ namespace cimg_library { 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 (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); + 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 default_light_texture; - static float olightx = 0, olighty = 0, olightz = 0, ospecular_shine = 0; + static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0; if (!default_light_texture || lightx!=olightx || lighty!=olighty || lightz!=olightz || - specular_shine!=ospecular_shine || default_light_texture._spectrum<_spectrum) { + 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 = (float)std::sqrt(dlx*dlx + dly*dly + dlz*dlz), - nlx = default_light_texture._width/2*(1 + dlx/nl), - nly = default_light_texture._height/2*(1 + dly/nl), + 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.0f,white); cimg_forXY(default_light_texture,x,y) { const float factor = default_light_texture(x,y); - if (factor>nspec) default_light_texture(x,y) = cimg::min(2,nsl1*factor*factor + nsl2*factor + nsl3); + if (factor>nspec) default_light_texture(x,y) = std::min(2.0f,nsl1*factor*factor + nsl2*factor + nsl3); } default_light_texture.resize(-100,-100,1,_spectrum); - olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shine = specular_shine; + olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess; } light_texture.assign(default_light_texture,true); } @@ -31481,7 +45537,9 @@ namespace cimg_library { CImg projections(vertices._width,2); tpfloat parallzmin = cimg::type::max(); const float absfocale = focale?cimg::abs(focale):0; - if (absfocale) cimg_forX(projections,l) { // Perspective projection + if (absfocale) { + cimg_pragma_openmp(parallel for cimg_openmp_if(projections.size()>4096)) + cimg_forX(projections,l) { // Perspective projection const tpfloat x = (tpfloat)vertices(l,0), y = (tpfloat)vertices(l,1), @@ -31489,7 +45547,10 @@ namespace cimg_library { const tpfloat projectedz = z + Z + absfocale; projections(l,1) = Y + absfocale*y/projectedz; projections(l,0) = X + absfocale*x/projectedz; - } else cimg_forX(projections,l) { // Parallel projection + } + } else { + cimg_pragma_openmp(parallel for cimg_openmp_if(projections.size()>4096)) + cimg_forX(projections,l) { // Parallel projection const tpfloat x = (tpfloat)vertices(l,0), y = (tpfloat)vertices(l,1), @@ -31498,21 +45559,31 @@ namespace cimg_library { projections(l,1) = Y + y; projections(l,0) = X + x; } + } - // Compute and sort visible primitives. - CImg visibles(primitives._width); + const float _focale = absfocale?absfocale:(1e5f-parallzmin); + float zmax = 0; + if (zbuffer) zmax = vertices.get_shared_row(2).max(); + + // Compute visible primitives. + CImg visibles(primitives._width,1,1,1,~0U); CImg zrange(primitives._width); - unsigned int nb_visibles = 0; const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type::min(); + bool is_forward = zbuffer?true:false; + + cimg_pragma_openmp(parallel for cimg_openmp_if(primitives.size()>4096)) cimglist_for(primitives,l) { const CImg& 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(nb_visibles) = (unsigned int)l; - zrange(nb_visibles++) = z0; + visibles(l) = (unsigned int)l; + zrange(l) = z0; } } break; case 5 : { // Sphere @@ -31520,15 +45591,25 @@ namespace cimg_library { 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=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) { - visibles(nb_visibles) = (unsigned int)l; - zrange(nb_visibles++) = (z0 + z1)/2; + 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 : // Segment case 6 : { @@ -31542,8 +45623,8 @@ namespace cimg_library { if (x0=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) { - visibles(nb_visibles) = (unsigned int)l; - zrange(nb_visibles++) = (z0 + z1)/2; + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1)/2; } } break; case 3 : // Triangle @@ -31565,9 +45646,9 @@ namespace cimg_library { 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 (double_sided || d<0) { - visibles(nb_visibles) = (unsigned int)l; - zrange(nb_visibles++) = (z0 + z1 + z2)/3; + if (is_double_sided || d<0) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1 + z2)/3; } } } break; @@ -31594,34 +45675,55 @@ namespace cimg_library { if (y2>yM) yM = y2; if (y3yM) yM = y3; - if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) { + 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 (double_sided || d<0) { - visibles(nb_visibles) = (unsigned int)l; - zrange(nb_visibles++) = (z0 + z1 + z2 + z3)/4; + 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 " + "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()); } } - if (nb_visibles<=0) return *this; + + // 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 permutations; - CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,false); + CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward); // Compute light properties CImg lightprops; switch (render_type) { case 3 : { // Flat Shading lightprops.assign(nb_visibles); + cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096)) cimg_forX(lightprops,l) { const CImg& primitive = primitives(visibles(permutations(l))); - const unsigned int psize = primitive.size(); + 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), @@ -31636,12 +45738,12 @@ namespace cimg_library { nx = dy1*dz2 - dz1*dy2, ny = dz1*dx2 - dx1*dz2, nz = dx1*dy2 - dy1*dx2, - norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + 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 = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz), - factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0); + 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; } @@ -31649,10 +45751,11 @@ namespace cimg_library { case 4 : // Gouraud Shading case 5 : { // Phong-Shading - CImg vertices_normals(vertices._width,3,1,1,0); + CImg vertices_normals(vertices._width,6,1,1,0); + cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096)) for (unsigned int l = 0; l& primitive = primitives[visibles(l)]; - const unsigned int psize = primitive.size(); + const unsigned int psize = (unsigned int)primitive.size(); const bool triangle_flag = (psize==3) || (psize==9), rectangle_flag = (psize==4) || (psize==12); @@ -31671,36 +45774,47 @@ namespace cimg_library { nnx = dy1*dz2 - dz1*dy2, nny = dz1*dx2 - dx1*dz2, nnz = dx1*dy2 - dy1*dx2, - norm = (tpfloat)(1e-5f + std::sqrt(nnx*nnx + nny*nny + nnz*nnz)), + norm = 1e-5f + cimg::hypot(nnx,nny,nnz), nx = nnx/norm, ny = nny/norm, nz = nnz/norm; - vertices_normals(i0,0)+=nx; vertices_normals(i0,1)+=ny; vertices_normals(i0,2)+=nz; - vertices_normals(i1,0)+=nx; vertices_normals(i1,1)+=ny; vertices_normals(i1,2)+=nz; - vertices_normals(i2,0)+=nx; vertices_normals(i2,1)+=ny; vertices_normals(i2,2)+=nz; - if (rectangle_flag) { vertices_normals(i3,0)+=nx; vertices_normals(i3,1)+=ny; vertices_normals(i3,2)+=nz; } + 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 (rectangle_flag) { + vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz; + } } } - if (double_sided) cimg_forX(vertices_normals,p) if (vertices_normals(p,2)>0) { - vertices_normals(p,0) = -vertices_normals(p,0); - vertices_normals(p,1) = -vertices_normals(p,1); - vertices_normals(p,2) = -vertices_normals(p,2); - } + 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(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 = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + 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 = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz), - factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0); + 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 { @@ -31708,12 +45822,13 @@ namespace cimg_library { 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(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 = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + norm = 1e-5f + cimg::hypot(nx,ny,nz), nnx = nx/norm, nny = ny/norm; lightprops(l,0) = lw2*(1 + nnx); @@ -31724,17 +45839,20 @@ namespace cimg_library { } // Draw visible primitives - const float _focale = absfocale?absfocale:(1-parallzmin); const CImg default_color(1,_spectrum,1,1,(tc)200); + CImg<_to> _opacity; + for (unsigned int l = 0; l& primitive = primitives[n_primitive]; const CImg &__color = n_primitive(), - _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?colors[n_primitive].get_resize(-100,-100,-100,_spectrum,0):CImg(), + _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)? + __color.get_resize(-100,-100,-100,_spectrum,0):CImg(), &color = _color?_color:(__color?__color:default_color); const tc *const pcolor = color._data; - const float opac = n_primitive=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &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)(color._width*factor), - _sh = (unsigned int)(color._height*factor), - sw = _sw?_sw:1, - sh = _sh?_sh:1; + _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<_width && sh<_height && (nx0+(int)sw/2>=0 || nx0-(int)sw/2=0 || ny0-(int)sh/2=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 - _sprite = (sw!=color._width || sh!=color._height)?color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), &sprite = _sprite?_sprite:color; - __draw_object3d(n_primitive,opacities,color,nx0,ny0,sprite,opac); + 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::None); - board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh); + board.setFillColor(LibBoard::Color::Null); + board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); } #endif } @@ -31790,21 +45936,21 @@ namespace cimg_library { 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,opac); - else draw_line(x0,y0,x1,y1,pcolor,opac); + 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)(opac*255)); - board.drawLine((float)x0,height()-(float)y0,x1,height()-(float)y1); + 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,opac).draw_point(x1,y1,pcolor,opac); + 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)(opac*255)); - board.drawCircle((float)x0,height()-(float)y0,0); - board.drawCircle((float)x1,height()-(float)y1,0); + 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 } @@ -31812,7 +45958,8 @@ namespace cimg_library { case 5 : { // Colored sphere const unsigned int n0 = (unsigned int)primitive[0], - n1 = (unsigned int)primitive[1]; + 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)), @@ -31820,73 +45967,82 @@ namespace cimg_library { zc = Z + Zc + _focale, xc = X + Xc*(absfocale?absfocale/zc:1), yc = Y + Yc*(absfocale?absfocale/zc:1), - radius = std::sqrt(cimg::sqr(Xc-vertices(n0,0)) + cimg::sqr(Yc-vertices(n0,1)) + cimg::sqr(Zc-vertices(n0,2)))*(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,opac); + draw_point((int)xc,(int)yc,pcolor,opacity); #ifdef cimg_use_board if (pboard) { - board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); - board.fillCircle(xc,height()-yc,0); + 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,opac,~0U); + 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)(opac*255)); - board.setFillColor(LibBoard::Color::None); - board.drawCircle(xc,height()-yc,radius); + 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 : - draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac); + 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)(opac*255)); - board.fillCircle(xc,height()-yc,radius); + 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 (!__color) { + if (render_type==5) cimg::mutex(10,0); throw CImgArgumentException(_cimg_instance - "draw_object3d() : Undefined texture for line primitive [%u].", + "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], - tx0 = (unsigned int)primitive[2], - ty0 = (unsigned int)primitive[3], - tx1 = (unsigned int)primitive[4], - ty1 = (unsigned int)primitive[5]; + 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,opac); - else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac); + 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)(opac*255)); - board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1); + 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,ty0)._data,opac). - draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac); + 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)(opac*255)); - board.drawCircle((float)x0,height()-(float)y0,0); - board.drawCircle((float)x1,height()-(float)y1,0); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); } #endif } @@ -31906,68 +46062,74 @@ namespace cimg_library { z2 = vertices(n2,2) + Z + _focale; switch (render_type) { case 0 : - draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).draw_point(x2,y2,pcolor,opac); + 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)(opac*255)); - board.drawCircle((float)x0,height()-(float)y0,0); - board.drawCircle((float)x1,height()-(float)y1,0); - board.drawCircle((float)x2,height()-(float)y2,0); + 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,opac).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opac). - draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac); + 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,opac).draw_line(x0,y0,x2,y2,pcolor,opac). - draw_line(x1,y1,x2,y2,pcolor,opac); + 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)(opac*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); + 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,opac); - else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac); + 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)(opac*255)); - board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + 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,opac,lightprops(l)); - else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l)); + 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 = cimg::min(lightprops(l),1); + const float lp = std::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(color[0]*lp), (unsigned char)(color[1]*lp), (unsigned char)(color[2]*lp), - (unsigned char)(opac*255)); - board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + (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),opac); - else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac); + 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)(opac*255)); - board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0), - (float)x1,height()-(float)y1,lightprops(n1), - (float)x2,height()-(float)y2,lightprops(n2)); + (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; @@ -31976,21 +46138,25 @@ namespace cimg_library { 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,opac); - else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac); + 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)))); + 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)(opac*255)); - board.fillGouraudTriangle((float)x0,height()-(float)y0,l0, - (float)x1,height()-(float)y1,l1, - (float)x2,height()-(float)y2,l2); + (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; @@ -32012,65 +46178,75 @@ namespace cimg_library { z1 = vertices(n1,2) + Z + _focale, z2 = vertices(n2,2) + Z + _focale, z3 = vertices(n3,2) + Z + _focale; + switch (render_type) { case 0 : - draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac). - draw_point(x2,y2,pcolor,opac).draw_point(x3,y3,pcolor,opac); + 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)(opac*255)); - board.drawCircle((float)x0,height()-(float)y0,0); - board.drawCircle((float)x1,height()-(float)y1,0); - board.drawCircle((float)x2,height()-(float)y2,0); - board.drawCircle((float)x3,height()-(float)y3,0); + 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,opac).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac). - draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opac).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opac); + 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,opac).draw_line(x1,y1,x2,y2,pcolor,opac). - draw_line(x2,y2,x3,y3,pcolor,opac).draw_line(x3,y3,x0,y0,pcolor,opac); + 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)(opac*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); + 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,opac).draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac); + 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,opac).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac); + 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)(opac*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); + 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,opac,lightprops(l)). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac,lightprops(l)); + 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,opac,lightprops(l)). - _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac,lightprops(l)); + _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 = cimg::min(lightprops(l),1); + const float lp = std::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(color[0]*lp), (unsigned char)(color[1]*lp), - (unsigned char)(color[2]*lp),(unsigned char)(opac*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); + (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; @@ -32079,23 +46255,23 @@ namespace cimg_library { lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opac). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opac); + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opacity); else - draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opac). - draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opac); + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opacity); #ifdef cimg_use_board if (pboard) { board.setPenColorRGBi((unsigned char)(color[0]), (unsigned char)(color[1]), (unsigned char)(color[2]), - (unsigned char)(opac*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); + (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; @@ -32106,49 +46282,48 @@ namespace cimg_library { lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); else - draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). - draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,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))); + 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)(opac*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); + (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 (!__color) { + if (render_type==5) cimg::mutex(10,0); throw CImgArgumentException(_cimg_instance - "draw_object3d() : Undefined texture for triangle primitive [%u].", + "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], - tx0 = (unsigned int)primitive[3], - ty0 = (unsigned int)primitive[4], - tx1 = (unsigned int)primitive[5], - ty1 = (unsigned int)primitive[6], - tx2 = (unsigned int)primitive[7], - ty2 = (unsigned int)primitive[8]; + 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); @@ -32158,71 +46333,81 @@ namespace cimg_library { z2 = vertices(n2,2) + Z + _focale; switch (render_type) { case 0 : - draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac). - draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac). - draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac); + 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)(opac*255)); - board.drawCircle((float)x0,height()-(float)y0,0); - board.drawCircle((float)x1,height()-(float)y1,0); - board.drawCircle((float)x2,height()-(float)y2,0); + 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,opac). - draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac). - draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac); + 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,opac). - draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac). - draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac); + 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)(opac*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); + 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,opac); - else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac); + 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)(opac*255)); - board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + 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,opac,lightprops(l)); - else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)); + 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 = cimg::min(lightprops(l),1); + const float lp = std::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(128*lp), (unsigned char)(128*lp), (unsigned char)(128*lp), - (unsigned char)(opac*255)); - board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2); + (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),opac); + 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),opac); + 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)(opac*255)); - board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0), - (float)x1,height()-(float)y1,lightprops(n1), - (float)x2,height()-(float)y2,lightprops(n2)); + 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; @@ -32232,45 +46417,48 @@ namespace cimg_library { (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), - opac); + 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), - opac); + 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)(opac*255)); - board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,(float)x1,height()-(float)y1,l1,(float)x2,height()-(float)y2,l2); + 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 (!__color) { + if (render_type==5) cimg::mutex(10,0); throw CImgArgumentException(_cimg_instance - "draw_object3d() : Undefined texture for quadrangle primitive [%u].", + "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], - tx0 = (unsigned int)primitive[4], - ty0 = (unsigned int)primitive[5], - tx1 = (unsigned int)primitive[6], - ty1 = (unsigned int)primitive[7], - tx2 = (unsigned int)primitive[8], - ty2 = (unsigned int)primitive[9], - tx3 = (unsigned int)primitive[10], - ty3 = (unsigned int)primitive[11]; + 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], 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), @@ -32283,72 +46471,84 @@ namespace cimg_library { switch (render_type) { case 0 : - draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac). - draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac). - draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac). - draw_point(x3,y3,color.get_vector_at(tx3,ty3)._data,opac); + 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)(opac*255)); - board.drawCircle((float)x0,height()-(float)y0,0); - board.drawCircle((float)x1,height()-(float)y1,0); - board.drawCircle((float)x2,height()-(float)y2,0); - board.drawCircle((float)x3,height()-(float)y3,0); + 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,opac). - draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac). - draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac). - draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac); + 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,opac). - draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac). - draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac). - draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac); + 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)(opac*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); + 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,opac). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); + 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,opac). - draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); + 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)(opac*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); + 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,opac,lightprops(l)). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); + 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,opac,lightprops(l)). - draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); + 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 = cimg::min(lightprops(l),1); + const float lp = std::min(lightprops(l),1); board.setPenColorRGBi((unsigned char)(128*lp), (unsigned char)(128*lp), (unsigned char)(128*lp), - (unsigned char)(opac*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); + (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; @@ -32357,20 +46557,24 @@ namespace cimg_library { lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); else - draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). - draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); #ifdef cimg_use_board if (pboard) { - board.setPenColorRGBi(128,128,128,(unsigned char)(opac*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); + 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; @@ -32381,25 +46585,29 @@ namespace cimg_library { lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); if (zbuffer) - draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). - draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); else - draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). - draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,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)(opac*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); + 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; @@ -32407,6 +46615,8 @@ namespace cimg_library { } break; } } + + if (render_type==5) cimg::mutex(10,0); return *this; } @@ -32417,150 +46627,170 @@ namespace cimg_library { //@{ //--------------------------- - //! Simple interface to select a shape from an image. + //! Launch simple interface to select a shape from an image. /** - \param selection Array of 6 values containing the selection result - \param feature_type Determine feature to select (0=point, 1=vector, 2=rectangle, 3=circle) - \param disp Display window used to make the selection - \param XYZ Initial XYZ position (for volumetric images only) - \param color Color of the shape selector. + \param disp Display window to use. + \param feature_type Type of feature to select. Can be { 0=point | 1=line | 2=rectangle | 3=ellipse }. + \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& select(CImgDisplay &disp, - const unsigned int feature_type=2, unsigned int *const XYZ=0) { - return get_select(disp,feature_type,XYZ).move_to(*this); + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) { + return get_select(disp,feature_type,XYZ,exit_on_anykey).move_to(*this); } - //! Simple interface to select a shape from an image. + //! Simple interface to select a shape from an image \overloading. CImg& select(const char *const title, - const unsigned int feature_type=2, unsigned int *const XYZ=0) { - return get_select(title,feature_type,XYZ).move_to(*this); + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) { + return get_select(title,feature_type,XYZ,exit_on_anykey).move_to(*this); } - //! Simple interface to select a shape from an image. + //! Simple interface to select a shape from an image \newinstance. CImg get_select(CImgDisplay &disp, - const unsigned int feature_type=2, unsigned int *const XYZ=0) const { - return _get_select(disp,0,feature_type,XYZ,0,0,0,true); + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false); } - //! Simple interface to select a shape from an image. + //! Simple interface to select a shape from an image \newinstance. CImg get_select(const char *const title, - const unsigned int feature_type=2, unsigned int *const XYZ=0) const { + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; - return _get_select(disp,title,feature_type,XYZ,0,0,0,true); + return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false); } - CImg _get_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 reset_view3d=true) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "select() : Empty instance.", - cimg_instance); - + CImg _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 { + if (is_empty()) return CImg(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 thumb; + if (width()>disp.screen_width() || height()>disp.screen_height()) + get_resize(cimg_fitscreen(width(),height(),1),1,-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(); + disp.show().set_key(0).set_wheel().show_mouse(); - unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; int area = 0, starting_area = 0, clicked_area = 0, phase = 0, - X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth), + 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, - X = -1, Y = -1, Z = -1, X3d = -1, Y3d = -1, - oX = X, oY = Y, oZ = Z, oX3d = X3d, oY3d = -1; + X3d = -1, Y3d = -1, + oX3d = X3d, oY3d = -1, + omx = -1, omy = -1; + float X = -1, Y = -1, Z = -1; unsigned int old_button = 0, key = 0; - bool shape_selected = false, text_down = false; + bool shape_selected = false, text_down = false, visible_cursor = true; static CImg pose3d; - static bool is_view3d = false; + static bool is_view3d = false, is_axes = true; if (reset_view3d) { pose3d.assign(); is_view3d = false; } CImg points3d, opacities3d, sel_opacities3d; CImgList primitives3d, sel_primitives3d; CImgList colors3d, sel_colors3d; CImg visu, visu0, view3d; - char text[1024] = { 0 }; + CImg text(1024); *text = 0; while (!key && !disp.is_closed() && !shape_selected) { // Handle mouse motion and selection - oX = X; oY = Y; oZ = Z; int mx = disp.mouse_x(), my = disp.mouse_y(); - const int - mX = mx<0?-1:mx*(width()+(depth()>1?depth():0))/disp.width(), - mY = my<0?-1:my*(height()+(depth()>1?depth():0))/disp.height(); + + const float + mX = mx<0?-1.0f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(), + mY = my<0?-1.0f:(float)my*(height() + (depth()>1?depth():0))/disp.height(); + area = 0; - if (mX>=0 && mY>=0 && mX=0 && mX=height()) { area = 2; X = mX; Z = mY - _height; Y = phase?Y1:Y0; } - if (mY>=0 && mX>=width() && mY=0 && mY>=0 && mX=0 && mX=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); } + if (mY>=0 && mX>=width() && mY=width() && mY>=height()) area = 4; if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0; + CImg 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::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; + 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; + 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; + 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 : is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); break; + 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; - char filename[32] = { 0 }; - std::FILE *file; - do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - if (visu0) { - visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp); - visu0.save(filename); - visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).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; - char filename[32] = { 0 }; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp); + visu0.save(filename); + (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,13,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,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); - visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp); + (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp); save(filename); - visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp); + (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data). + display(disp); disp.set_key(key,false); key = 0; } break; } @@ -32568,45 +46798,47 @@ namespace cimg_library { switch (area) { case 0 : // When mouse is out of image range. - mx = my = X = Y = Z = -1; + mx = my = -1; X = Y = Z = -1; break; case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections. if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step). - if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign(); - X1 = X; Y1 = Y; Z1 = Z; + if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; } if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes). switch (starting_area) { - case 1 : if (Z1!=Z) visu0.assign(); Z1 = Z; break; - case 2 : if (Y1!=Y) visu0.assign(); Y1 = Y; break; - case 3 : if (X1!=X) visu0.assign(); X1 = X; break; + 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 (disp.button()&2 && clicked_area==area) { // When moving through the image/volume. if (phase) { - if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign(); - X1 = X; Y1 = Y; Z1 = Z; + 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!=X || Y0!=Y || Z0!=Z)) visu0.assign(); - X0 = X; Y0 = Y; Z0 = Z; + if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign(); + X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; } } - if (disp.button()&4) { // Reset positions. - oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = area = clicked_area = starting_area = 0; visu0.assign(); + if (disp.button()&4) { + X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = clicked_area = starting_area = 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() && + if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && + !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() && !disp.is_keyALT() && !disp.is_keyALTGR()) { switch (area) { case 1 : - if (phase) Z = (Z1+=disp.wheel()); else Z = (Z0+=disp.wheel()); + if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel()); visu0.assign(); break; case 2 : - if (phase) Y = (Y1+=disp.wheel()); else Y = (Y0+=disp.wheel()); + if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel()); visu0.assign(); break; case 3 : - if (phase) X = (X1+=disp.wheel()); else X = (X0+=disp.wheel()); + if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel()); visu0.assign(); break; } disp.set_wheel(); @@ -32616,12 +46848,12 @@ namespace cimg_library { switch (phase) { case 0 : if (area==clicked_area) { - X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; starting_area = area; ++phase; + X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; starting_area = area; ++phase; } break; case 1 : if (area==starting_area) { - X1 = X; Y1 = Y; Z1 = Z; ++phase; - } else if (!(disp.button()&1)) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu0.assign(); } + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase; + } else if (!(disp.button()&1)) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); } break; case 2 : ++phase; break; } @@ -32631,45 +46863,46 @@ namespace cimg_library { 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)); + 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; } - if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } // Left + right buttons : reset. - else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button : rotate. + // 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*cimg::min(view3d._width,view3d._height), + 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 = (float)std::sqrt(u0*u0+v0*v0), - n1 = (float)std::sqrt(u1*u1+v1*v1), + 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(cimg::max(0,R2-nu0*nu0-nv0*nv0)), + nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)), nu1 = n1>R?(u1*R/n1):u1, nv1 = n1>R?(v1*R/n1):v1, - nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)), + nw1 = (float)std::sqrt(std::max(0.0f,R2 - nu1*nu1 - nv1*nv1)), u = nv0*nw1 - nw0*nv1, v = nw0*nu1 - nu0*nw1, w = nv0*nu1 - nu0*nv1, - n = (float)std::sqrt(u*u+v*v+w*w), - alpha = (float)std::asin(n/R2); - pose3d.draw_image(CImg::rotation_matrix(u,v,w,alpha)*pose3d.get_crop(0,0,2,2)); + n = cimg::hypot(u,v,w), + alpha = (float)std::asin(n/R2)*180/cimg::PI; + pose3d.draw_image(CImg::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. + } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom. pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign(); } - if (disp.wheel()) { // Wheel : zoom + 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. + 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 = X = Y = Z = -1; + mx = my = -1; X = Y = Z = -1; break; } @@ -32681,36 +46914,25 @@ namespace cimg_library { } } - 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; + 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 (oX!=X || oY!=Y || oZ!=Z || !visu0 || (_depth>1 && !view3d)) { + if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { if (!visu0) { // Create image of projected planes. - CImg tmp, tmp0; - if (_depth!=1) { - tmp0 = get_projections2d(phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0); - tmp = tmp0.get_channels(0,cimg::min(2U,_spectrum - 1)); - } else tmp = get_channels(0,cimg::min(2U,_spectrum - 1)); - switch (old_normalization) { - case 0 : tmp.move_to(visu0); break; - case 1 : tmp.normalize(0,255).move_to(visu0); break; - case 2 : { - const float m = disp._min, M = disp._max; - ((tmp-=m)*=255.0f/(M-m>0?M-m:1)).move_to(visu0); - } - case 3 : - if (cimg::type::is_float()) (tmp.normalize(0,255)).move_to(visu0); - else { - const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); - ((tmp-=m)*=255.0f/(M-m)).move_to(visu0); - } break; - } + 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(); @@ -32718,17 +46940,18 @@ namespace cimg_library { 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(1,2,1,1,64,128).resize(visu0._width-x3d,visu0._height-y3d,1,visu0._spectrum,3).move_to(view3d); + _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(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(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'); + 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::vector(12,13).move_to(primitives3d); CImg::vector(13,14).move_to(primitives3d); CImg::vector(14,15).move_to(primitives3d); CImg::vector(15,12).move_to(primitives3d); CImg::vector(16,17).move_to(primitives3d); CImg::vector(17,18).move_to(primitives3d); @@ -32749,12 +46972,18 @@ namespace cimg_library { Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1, Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x'); sel_primitives3d.assign(); - CImg::vector(20,21).move_to(sel_primitives3d); CImg::vector(21,22).move_to(sel_primitives3d); - CImg::vector(22,23).move_to(sel_primitives3d); CImg::vector(23,20).move_to(sel_primitives3d); - CImg::vector(24,25).move_to(sel_primitives3d); CImg::vector(25,26).move_to(sel_primitives3d); - CImg::vector(26,27).move_to(sel_primitives3d); CImg::vector(27,24).move_to(sel_primitives3d); - CImg::vector(20,24).move_to(sel_primitives3d); CImg::vector(21,25).move_to(sel_primitives3d); - CImg::vector(22,26).move_to(sel_primitives3d); CImg::vector(23,27).move_to(sel_primitives3d); + CImg::vector(20,21).move_to(sel_primitives3d); + CImg::vector(21,22).move_to(sel_primitives3d); + CImg::vector(22,23).move_to(sel_primitives3d); + CImg::vector(23,20).move_to(sel_primitives3d); + CImg::vector(24,25).move_to(sel_primitives3d); + CImg::vector(25,26).move_to(sel_primitives3d); + CImg::vector(26,27).move_to(sel_primitives3d); + CImg::vector(27,24).move_to(sel_primitives3d); + CImg::vector(20,24).move_to(sel_primitives3d); + CImg::vector(21,25).move_to(sel_primitives3d); + CImg::vector(22,26).move_to(sel_primitives3d); + CImg::vector(23,27).move_to(sel_primitives3d); } else { points3d.append(CImg(2,3,1,1, X0,X1, @@ -32765,8 +46994,8 @@ namespace cimg_library { sel_colors3d.assign(sel_primitives3d._width,CImg::vector(255,255,255)); sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f); } - points3d.shift_object3d(-0.5f*_width,-0.5f*_height,-0.5f*_depth).resize_object3d(); - points3d*=0.75f*cimg::min(view3d._width,view3d._height); + 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(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d); @@ -32787,119 +47016,193 @@ namespace cimg_library { } visu = visu0; - const int d = (_depth>1)?_depth:0; - if (phase) switch (feature_type) { - case 1 : { - const int - x0 = (int)((X0+0.5f)*disp.width()/(_width+d)), - y0 = (int)((Y0+0.5f)*disp.height()/(_height+d)), - x1 = (int)((X1+0.5f)*disp.width()/(_width+d)), - y1 = (int)((Y1+0.5f)*disp.height()/(_height+d)); - visu.draw_arrow(x0,y0,x1,y1,background_color,0.9f,30,5,0x55555555). - draw_arrow(x0,y0,x1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA); - if (d) { - const int - zx0 = (int)((_width+Z0+0.5f)*disp.width()/(_width+d)), - zx1 = (int)((_width+Z1+0.5f)*disp.width()/(_width+d)), - zy0 = (int)((_height+Z0+0.5f)*disp.height()/(_height+d)), - zy1 = (int)((_height+Z1+0.5f)*disp.height()/(_height+d)); - visu.draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0x55555555). - draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0x55555555). - draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA). - draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0xAAAAAAAA); + 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, + 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.0f)*w/W - 1), xn = _xn + ((int)((_xn + 1.0f)*W/w)!=_vX + 1), + _yn = (int)((_vY + 1.0f)*h/H - 1), yn = _yn + ((int)((_yn + 1.0f)*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.0f)*w/W - 1), + zxn = _zxn + ((int)((_zxn + 1.0f)*W/w)!=_vZ + width() + 1), + _zyn = (int)((_vZ + height() + 1.0f)*h/H - 1), + zyn = _zyn + ((int)((_zyn + 1.0f)*H/h)!=_vZ + height() + 1), + _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.0f)*W/w)!=width()), + _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.0f)*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); } - } break; - case 2 : { - const int - x0 = (X0=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); } - } break; - case 3 : { - const int - x0 = X0*disp.width()/(_width+d), - y0 = Y0*disp.height()/(_height+d), - x1 = X1*disp.width()/(_width+d)-1, - y1 = Y1*disp.height()/(_height+d)-1; - visu.draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,background_color,0.2f). - draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555); - if (d) { + + // Draw selection. + if (phase) { const int - zx0 = (int)((_width+Z0)*disp.width()/(_width+d)), - zy0 = (int)((_height+Z0)*disp.height()/(_height+d)), - zx1 = (int)((_width+Z1+1)*disp.width()/(_width+d))-1, - zy1 = (int)((_height+Z1+1)*disp.height()/(_height+d))-1; - visu.draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,background_color,0.2f). - draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555). - draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,background_color,0.2f). - draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,foreground_color,0.6f,0x55555555); + _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.0f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.0f)*W/w)!=X0 + 1), + _yn0 = (int)((Y0 + 1.0f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.0f)*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.0f)*w/W - 1), + zxn0 = _zxn0 + ((int)((_zxn0 + 1.0f)*W/w)!=Z0 + width() + 1), + _zyn0 = (int)((Z0 + height() + 1.0f)*h/H - 1), + zyn0 = _zyn0 + ((int)((_zyn0 + 1.0f)*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=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f). - draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,~0U); + + // 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 && X1 || 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); + char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512; + for (unsigned int c = 0; c<_spectrum && ctext::format_s(), + cimg::type::format((*this)((int)X,(int)Y,(int)Z,c))); + ctext = text._data + std::strlen(text); + *(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), + norm = cimg::hypot(dX,dY,dZ); + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,norm); + else cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Norm = %g ", + origX + X0,origY + Y0,origX + X1,origY + Y1,norm); + } break; + case 2 : + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ", + origX + (X01 || 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(0,text_down?visu.height() - 13:0,text,foreground_color,background_color,0.7f,13); } - 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 && X1) cimg_snprintf(text,sizeof(text)," Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z); - else cimg_snprintf(text,sizeof(text)," Point (%d,%d) = [ ",origX+X,origY+Y); - char *ctext = text + std::strlen(text), *const ltext = text + 512; - for (unsigned int c = 0; c<_spectrum && ctext::format(),cimg::type::format((*this)(X,Y,Z,c))); - ctext = text + std::strlen(text); - *(ctext++) = ' '; *ctext = 0; - } - std::strcpy(text + std::strlen(text),"] "); - } - } else switch (feature_type) { - case 1 : { - const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = std::sqrt(dX*dX+dY*dY+dZ*dZ); - if (_depth>1) cimg_snprintf(text,sizeof(text)," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ", - origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm); - else cimg_snprintf(text,sizeof(text)," Vect (%d,%d)-(%d,%d), Norm = %g ", - origX+X0,origY+Y0,origX+X1,origY+Y1,norm); - } break; - case 2 : - if (_depth>1) cimg_snprintf(text,sizeof(text)," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ", - origX+(X01) cimg_snprintf(text,sizeof(text)," 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,sizeof(text)," 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(0,text_down?visu.height()-13:0,text,foreground_color,background_color,0.7f,13); disp.display(visu).wait(); } else 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 + // Return result. CImg 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) { @@ -32910,55 +47213,121 @@ namespace cimg_library { } if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; switch (feature_type) { - case 1 : case 2 : res[3] = X1; res[4] = Y1; res[5] = Z1; + 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); // keep no break here! default : res[0] = X0; res[1] = Y0; res[2] = Z0; } } - disp.set_button(); + 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 __get_select(const CImgDisplay& disp, const int normalization, + const int x, const int y, const int z) const { + if (is_empty()) return CImg(1,1,1,1,0); + const CImg crop = get_shared_channels(0,std::min(2,spectrum() - 1)); + CImg 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(crop,false).move_to(img2d); + + // Check for inf and NaN values. + if (cimg::type::is_float() && normalization) { + bool is_inf = false, is_nan = false; + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) { is_inf = true; break; } + else if (cimg::type::is_nan(*ptr)) { is_nan = true; break; } + if (is_inf || is_nan) { + Tint m0 = (Tint)cimg::type::max(), M0 = (Tint)cimg::type::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::is_inf(*ptr) && !cimg::type::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::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values. + if (is_inf) + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values. + } + } + + switch (normalization) { + case 1 : img2d.normalize((T)0,(T)255); break; + case 2 : { + const float m = disp._min, M = disp._max; + (img2d-=m)*=255.0f/(M - m>0?M - m:1); + } break; + case 3 : + if (cimg::type::is_float()) img2d.normalize((T)0,(T)255); + else { + const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); + (img2d-=m)*=255.0f/(M - m>0?M - m:1); + } break; + } + + if (img2d.spectrum()==2) img2d.channels(0,2); + return img2d; + } + //! Select sub-graph in a graph. CImg 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 { + 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.", + "select_graph(): Empty instance.", cimg_instance); - if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type()); - const unsigned int siz = _width*_height*_depth, old_normalization = disp.normalization(); + 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); + 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.0; } - const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; - const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 }; + 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 }; static unsigned int odimv = 0; - static CImg palette; + static CImg colormap; if (odimv!=_spectrum) { odimv = _spectrum; - palette = CImg(3,_spectrum,1,1,120).noise(70,1); - if (_spectrum==1) { palette[0] = palette[1] = 120; palette[2] = 200; } + colormap = CImg(3,_spectrum,1,1,120).noise(70,1); + if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; } else { - palette(0,0) = 220; palette(1,0) = 10; palette(2,0) = 10; - if (_spectrum>1) { palette(0,1) = 10; palette(1,1) = 220; palette(2,1) = 10; } - if (_spectrum>2) { palette(0,2) = 10; palette(1,2) = 10; palette(2,2) = 220; } + 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; } } } CImg 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?0:1; + const unsigned int one = plot_type==3?0U:1U; unsigned int okey = 0, obutton = 0; - char message[1024] = { 0 }; + CImg message(1024); CImg_3x3(I,unsigned char); for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) { @@ -32971,40 +47340,47 @@ namespace cimg_library { 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) 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),&palette(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f, - plot_type,vertex_type,nymax,nymin); + 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.0,(int)std::log10(dx)-2.0), - py = (float)std::pow(10.0,(int)std::log10(dy)-2.0); + dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin), + px = (float)std::pow(10.0,(int)std::log10(dx?dx:1) - 2.0), + py = (float)std::pow(10.0,(int)std::log10(dy?dy:1) - 2.0); const CImg - seqx = CImg::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin+(nxmax-nxmin)*(siz+1)/siz).round(px), + seqx = dx<=0?CImg::vector(nxmin): + CImg::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz).round(px), seqy = CImg::sequence(1 + gdimy/60,nymax,nymin).round(py); - axes.draw_axes(seqx,seqy,white); - if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray); - if (nymax<0) axes.draw_axis(seqx,0,gray); - if (nxmin>0) axes.draw_axis(0,seqy,gray); - if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray); + + 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) = (graph(x,y,c)+255)/2; + 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); + 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); + 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); + visu0.draw_image(1,(visu0.height() - text.height())/2,~text); visu.assign(); } @@ -33017,108 +47393,115 @@ namespace cimg_library { nx1 = x0<=x1?x1:x0, ny0 = y0<=y1?y0:y1, ny1 = y0<=y1?y1:y0, - sx0 = 16 + nx0*(visu.width()-32)/(siz-one), - sx1 = 15 + (nx1+1)*(visu.width()-32)/(siz-one), + 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); + 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=16 && mouse_y>=16 && mouse_x=7) - cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx, + 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)); + (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,sizeof(message),"Value[%u:%g] = ( ",x,cx); - cimg_forC(*this,c) std::sprintf(message + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); - std::sprintf(message + std::strlen(message),")"); + 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 = x0<=x1?x0:x1, - nx1 = x0<=x1?x1:x0, - ny0 = y0<=y1?y0:y1, - ny1 = y0<=y1?y1:y0; + 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)/(siz-1), - cx1 = nxmin + (nx1+one)*(nxmax-nxmin)/(siz-1), - cy0 = nymax - ny0*(nymax-nymin)/(visu._height-32), - cy1 = nymax - ny1*(nymax-nymin)/(visu._height-32); + 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) - std::sprintf(message + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",x0,cx0,cy0,x1+one,cx1,cy1); + cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", + x0,cx0,cy0,x1 + one,cx1,cy1); else - std::sprintf(message + std::strlen(message)," - Range [ %u:%g - %u:%g ]",x0,cx0,x1+one,cx1); + 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.draw_image((visu.width() - text.width())/2,1,~text); } visu.display(disp); } // Test keys. + CImg 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). + 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_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(640,480,1),false)._is_resized = true; - disp.set_key(key,false); okey = 0; - } break; + 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; + 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 &screen = visu?visu:visu0; - char filename[32] = { 0 }; - std::FILE *file; - do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); - } while (file); - (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp); - screen.save(filename); - screen.draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp); - } - disp.set_key(key,false); okey = 0; - } break; + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp); + screen.save(filename); + (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,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 &screen = visu?visu:visu0; - char filename[32] = { 0 }; std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp); save(filename); - screen.draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename).display(disp); + (+screen).draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename._data).display(disp); } disp.set_key(key,false); okey = 0; } break; @@ -33129,10 +47512,10 @@ namespace cimg_library { visu.assign(); if (disp.mouse_x()>=0 && disp.mouse_y()>=0) { const int - mx = (mouse_x -16)*(int)(siz-one)/(disp.width()-32), - cx = mx<0?0:(mx>=(int)(siz-one)?siz-1-one:mx), + mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32), + cx = cimg::cut(mx,0,(int)(siz - 1 - one)), my = mouse_y - 16, - cy = my<=0?0:(my>=(disp.height()-32)?(disp.height()-32):my); + cy = cimg::cut(my,0,disp.height() - 32); if (button&1) { if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; } } @@ -33145,37 +47528,43 @@ namespace cimg_library { } 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(4,1,1,1,x0,y0,x1>=0?x1+(int)one:-1,y1); + return CImg(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1); } - //! Load an image from a file. + //! Load image from a file. /** - \param filename is the name of the image file to load. + \param filename Filename, as a C-string. \note The extension of \c filename defines the file format. If no filename - extension is provided, CImg::get_load() will try to load a .cimg file. + extension is provided, CImg::get_load() will try to load the file as a .cimg or .cimgz file. **/ CImg& load(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load() : Specified filename is (null).", + "load(): Specified filename is (null).", cimg_instance); - if (!cimg::strncasecmp(filename,"http://",7)) { - char filename_local[1024] = { 0 }; - load(cimg::load_network_external(filename,filename_local)); + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg 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; + cimg::exception_mode(0); + bool is_loaded = true; try { #ifdef cimg_load_plugin cimg_load_plugin(filename); @@ -33204,7 +47593,7 @@ namespace cimg_library { #ifdef cimg_load_plugin8 cimg_load_plugin8(filename); #endif - // ASCII formats + // Ascii formats if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename); else if (!cimg::strcasecmp(ext,"dlm") || !cimg::strcasecmp(ext,"txt")) load_dlm(filename); @@ -33236,6 +47625,7 @@ namespace cimg_library { !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") || @@ -33265,72 +47655,91 @@ namespace cimg_library { !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_ffmpeg(filename); - else throw CImgIOException("CImg<%s>::load()", - pixel_type()); - } catch (CImgException& e) { - if (!cimg::strncasecmp(e.what(),"cimg::fopen()",13)) { - cimg::exception_mode() = omode; + !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 = std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); throw CImgIOException(_cimg_instance - "load() : Failed to open file '%s'.", + "load(): Failed to open file '%s'.", cimg_instance, filename); - } else try { - const char *const f_type = cimg::file_type(0,filename); + } + + const char *const f_type = cimg::ftype(file,filename); + std::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,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename); - else throw CImgIOException("CImg<%s>::load()", - pixel_type()); - } catch (CImgException&) { - try { - load_other(filename); - } catch (CImgException&) { - throw CImgIOException(_cimg_instance - "load() : Failed to recognize format of file '%s'.", - cimg_instance, - 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; + cimg::exception_mode(omode); return *this; } + //! Load image from a file \newinstance. static CImg get_load(const char *const filename) { return CImg().load(filename); } - //! Load an image from an ASCII file. + //! Load image from an ascii file. + /** + \param filename Filename, as a C -string. + **/ CImg& load_ascii(const char *const filename) { return _load_ascii(0,filename); } + //! Load image from an ascii file \inplace. static CImg get_load_ascii(const char *const filename) { return CImg().load_ascii(filename); } - //! Load an image from an ASCII file. + //! Load image from an ascii file \overloading. CImg& load_ascii(std::FILE *const file) { return _load_ascii(file,0); } + //! Loadimage from an ascii file \newinstance. static CImg get_load_ascii(std::FILE *const file) { return CImg().load_ascii(file); } @@ -33338,54 +47747,61 @@ namespace cimg_library { CImg& _load_ascii(std::FILE *const file, const char *const filename) { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_ascii() : Specified filename is (null).", + "load_ascii(): Specified filename is (null).", cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - char line[256] = { 0 }; - int err = std::fscanf(nfile,"%255[^\n]",line); - unsigned int off, dx = 0, dy = 1, dz = 1, dc = 1; - std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); - err = std::fscanf(nfile,"%*[^0-9.eE+-]"); + CImg 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).", + "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 unsigned int siz = size(); + const ulongT siz = size(); + ulongT off = 0; double val; T *ptr = _data; for (err = 1, off = 0; off& load_dlm(const char *const filename) { return _load_dlm(0,filename); } + //! Load image from a DLM file \newinstance. static CImg get_load_dlm(const char *const filename) { return CImg().load_dlm(filename); } - //! Load an image from a DLM file. + //! Load image from a DLM file \overloading. CImg& load_dlm(std::FILE *const file) { return _load_dlm(file,0); } + //! Load image from a DLM file \newinstance. static CImg get_load_dlm(std::FILE *const file) { return CImg().load_dlm(file); } @@ -33393,21 +47809,21 @@ namespace cimg_library { CImg& _load_dlm(std::FILE *const file, const char *const filename) { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_dlm() : Specified filename is (null).", + "load_dlm(): Specified filename is (null).", cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); - char delimiter[256] = { 0 }, tmp[256] = { 0 }; + CImg delimiter(256), tmp(256); *delimiter = *tmp = 0; unsigned int cdx = 0, dx = 0, dy = 0; int err = 0; double val; - assign(256,256); - while ((err = std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))>0) { + 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 (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') { - dx = cimg::max(cdx,dx); + 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; } @@ -33416,7 +47832,7 @@ namespace cimg_library { if (!dx || !dy) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimg_instance - "load_dlm() : Invalid DLM file '%s'.", + "load_dlm(): Invalid DLM file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } @@ -33425,20 +47841,25 @@ namespace cimg_library { return *this; } - //! Load an image from a BMP file. + //! Load image from a BMP file. + /** + \param filename Filename, as a C-string. + **/ CImg& load_bmp(const char *const filename) { return _load_bmp(0,filename); } + //! Load image from a BMP file \newinstance. static CImg get_load_bmp(const char *const filename) { return CImg().load_bmp(filename); } - //! Load an image from a BMP file. + //! Load image from a BMP file \overloading. CImg& load_bmp(std::FILE *const file) { return _load_bmp(file,0); } + //! Load image from a BMP file \newinstance. static CImg get_load_bmp(std::FILE *const file) { return CImg().load_bmp(file); } @@ -33446,16 +47867,16 @@ namespace cimg_library { CImg& _load_bmp(std::FILE *const file, const char *const filename) { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_bmp() : Specified filename is (null).", + "load_bmp(): Specified filename is (null).", cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - unsigned char header[64] = { 0 }; - cimg::fread(header,54,nfile); + CImg 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'.", + "load_bmp(): Invalid BMP file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } @@ -33464,47 +47885,65 @@ namespace cimg_library { 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); - const int - cimg_iobuffer = 12*1024*1024, - dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)), - align_bytes = (4-dx_bytes%4)%4, - buf_size = cimg::min(cimg::abs(dy)*(dx_bytes + align_bytes),file_size - offset); - CImg palette; + 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)):(dx*bpp/8)), + align_bytes = (4 - dx_bytes%4)%4; + const longT + cimg_iobuffer = (longT)24*1024*1024, + buf_size = std::min((longT)cimg::abs(dy)*(dx_bytes + align_bytes),(longT)file_size - offset); + + CImg colormap; if (bpp<16) { if (!nb_colors) nb_colors = 1<0) std::fseek(nfile,xoffset,SEEK_CUR); + 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 buffer; - if (buf_size=0; --y) { - if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + 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*)(palette._data + (val&mask?1:0)); + 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++); @@ -33514,13 +47953,16 @@ namespace cimg_library { } } break; case 4 : { // 16 colors - for (int y = height()-1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + 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*)(palette._data + color); + 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++); @@ -33530,10 +47972,13 @@ namespace cimg_library { } } break; case 8 : { // 256 colors - for (int y = height()-1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } cimg_forX(*this,x) { - const unsigned char *col = (unsigned char*)(palette._data + *(ptrs++)); + 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++); @@ -33542,8 +47987,11 @@ namespace cimg_library { } } break; case 16 : { // 16 bits colors - for (int y = height()-1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + 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)); @@ -33555,8 +48003,11 @@ namespace cimg_library { } } break; case 24 : { // 24 bits colors - for (int y = height()-1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } cimg_forX(*this,x) { (*this)(x,y,2) = (T)*(ptrs++); (*this)(x,y,1) = (T)*(ptrs++); @@ -33566,8 +48017,11 @@ namespace cimg_library { } } break; case 32 : { // 32 bits colors - for (int y = height()-1; y>=0; --y) { - if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); } + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } cimg_forX(*this,x) { (*this)(x,y,2) = (T)*(ptrs++); (*this)(x,y,1) = (T)*(ptrs++); @@ -33583,20 +48037,25 @@ namespace cimg_library { return *this; } - //! Load an image from a JPEG file. + //! Load image from a JPEG file. + /** + \param filename Filename, as a C-string. + **/ CImg& load_jpeg(const char *const filename) { return _load_jpeg(0,filename); } + //! Load image from a JPEG file \newinstance. static CImg get_load_jpeg(const char *const filename) { return CImg().load_jpeg(filename); } - //! Load an image from a JPEG file. + //! Load image from a JPEG file \overloading. CImg& load_jpeg(std::FILE *const file) { return _load_jpeg(file,0); } + //! Load image from a JPEG file \newinstance. static CImg get_load_jpeg(std::FILE *const file) { return CImg().load_jpeg(file); } @@ -33622,50 +48081,54 @@ namespace cimg_library { CImg& _load_jpeg(std::FILE *const file, const char *const filename) { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_jpeg() : Specified filename is (null).", + "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.", + "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.", + "load_jpeg(): Error message returned by libjpeg: %s.", cimg_instance,jerr.message); } - std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); 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) return load_other(filename); - else + if (!file) { + cimg::fclose(nfile); + return load_other(filename); + } else throw CImgIOException(_cimg_instance - "load_jpeg() : Failed to load JPEG data from file '%s'.", + "load_jpeg(): Failed to load JPEG data from file '%s'.", cimg_instance,filename?filename:"(FILE*)"); } CImg buffer(cinfo.output_width*cinfo.output_components); JSAMPROW row_pointer[1]; - assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); - T *ptr_r = _data, *ptr_g = _data + _width*_height, *ptr_b = _data + 2*_width*_height, *ptr_a = _data + 3*_width*_height; + 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 // This is experimental code, not much tested, use with care. CImg& load_magick(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_magick() : Specified filename is (null).", + "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(); @@ -33717,7 +48182,7 @@ namespace cimg_library { 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 (unsigned int off = W*H; off; --off) { + for (ulongT off = (ulongT)W*H; off; --off) { *(ptr_r++) = (T)(pixels->red); *(ptr_g++) = (T)(pixels->green); *(ptr_b++) = (T)(pixels->blue); @@ -33730,7 +48195,7 @@ namespace cimg_library { 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 (unsigned int off = W*H; off; --off) { + for (ulongT off = (ulongT)W*H; off; --off) { *(ptr_r++) = (T)(pixels->red); *(ptr_g++) = (T)(pixels->green); *(ptr_b++) = (T)(pixels->blue); @@ -33741,7 +48206,7 @@ namespace cimg_library { 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 (unsigned int off = W*H; off; --off) { + for (ulongT off = (ulongT)W*H; off; --off) { *(ptr_r++) = (T)(pixels->red); *(ptr_a++) = (T)(pixels->opacity); ++pixels; @@ -33751,68 +48216,80 @@ namespace cimg_library { assign(W,H,1,1); T *ptr_r = data(0,0,0,0); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); - for (unsigned int off = W*H; off; --off) { + 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.", + "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.", cimg_instance, filename); #endif - return *this; } + //! Load image from a file, using Magick++ library \newinstance. static CImg get_load_magick(const char *const filename) { return CImg().load_magick(filename); } - //! Load an image from a PNG file. - CImg& load_png(const char *const filename) { - return _load_png(0,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& load_png(const char *const filename, unsigned int *const bits_per_pixel=0) { + return _load_png(0,filename,bits_per_pixel); } - static CImg get_load_png(const char *const filename) { - return CImg().load_png(filename); + //! Load image from a PNG file \newinstance. + static CImg get_load_png(const char *const filename, unsigned int *const bits_per_pixel=0) { + return CImg().load_png(filename,bits_per_pixel); } - //! Load an image from a PNG file. - CImg& load_png(std::FILE *const file) { - return _load_png(file,0); + //! Load image from a PNG file \overloading. + CImg& load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) { + return _load_png(file,0,bits_per_pixel); } - static CImg get_load_png(std::FILE *const file) { - return CImg().load_png(file); + //! Load image from a PNG file \newinstance. + static CImg get_load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) { + return CImg().load_png(file,bits_per_pixel); } - // (Note : Most of this function has been written by Eric Fausett) - CImg& _load_png(std::FILE *const file, const char *const filename) { + // (Note: Most of this function has been written by Eric Fausett) + CImg& _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).", + "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.", + "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 - const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'. +#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'.", + "load_png(): Invalid PNG file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -33824,7 +48301,7 @@ namespace cimg_library { if (!png_ptr) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimg_instance - "load_png() : Failed to initialize 'png_ptr' structure for file '%s'.", + "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -33833,7 +48310,7 @@ namespace cimg_library { 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'.", + "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -33842,7 +48319,7 @@ namespace cimg_library { 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'.", + "load_png(): Failed to initialize 'end_info' structure for file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -33852,7 +48329,7 @@ namespace cimg_library { 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'.", + "load_png(): Encountered unknown fatal error in libpng for file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -33865,6 +48342,7 @@ namespace cimg_library { 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) { @@ -33874,7 +48352,6 @@ namespace cimg_library { } if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) { png_set_expand_gray_1_2_4_to_8(png_ptr); - color_type = PNG_COLOR_TYPE_RGB; is_gray = true; bit_depth = 8; } @@ -33895,7 +48372,7 @@ namespace cimg_library { 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'.", + "load_png(): Invalid bit depth %u in file '%s'.", cimg_instance, bit_depth,nfilename?nfilename:"(FILE*)"); } @@ -33903,7 +48380,7 @@ namespace cimg_library { // Allocate Memory for Image Read png_bytep *const imgData = new png_bytep[H]; - for (unsigned int row = 0; row& load_pnm(const char *const filename) { return _load_pnm(0,filename); } + //! Load image from a PNM file \newinstance. static CImg get_load_pnm(const char *const filename) { return CImg().load_pnm(filename); } - //! Load an image from a PNM file. + //! Load image from a PNM file \overloading. CImg& load_pnm(std::FILE *const file) { return _load_pnm(file,0); } + //! Load image from a PNM file \newinstance. static CImg get_load_pnm(std::FILE *const file) { return CImg().load_pnm(file); } @@ -33979,36 +48462,36 @@ namespace cimg_library { CImg& _load_pnm(std::FILE *const file, const char *const filename) { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_pnm() : Specified filename is (null).", + "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; - char item[1024] = { 0 }; + CImg item(16384,1,1,1,0); int err, rval, gval, bval; - const long cimg_iobuffer = 12*1024*1024; - while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item," P%u",&ppm_type)!=1) { + 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'.", + "load_pnm(): PNM header not found in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } - while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if ((err=std::sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) { + 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'.", + "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==8 || ppm_type==9))) { - while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item,"%u",&colormax)!=1) + 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'.", + "load_pnm(): COLORMAX field is undefined in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } else { colormax = D; D = 1; } @@ -34030,8 +48513,9 @@ namespace cimg_library { 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; + 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). @@ -34039,42 +48523,42 @@ namespace cimg_library { assign(W,H,D,1); T *ptrd = data(0,0,0,0); unsigned int w = 0, h = 0, d = 0; - for (long to_read = (long)((W/8 + (W%8?1:0))*H*D); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer)); + 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 (unsigned long off = (unsigned long)raw._width; off || mask; mask>>=1) { + 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 : { // 2d/3d grey binary (support 3D PINK extension). + case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension). if (colormax<256) { // 8 bits. CImg raw; assign(W,H,D,1); T *ptrd = data(0,0,0,0); - for (long to_read = (long)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer)); + 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 (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); } } else { // 16 bits. CImg raw; assign(W,H,D,1); T *ptrd = data(0,0,0,0); - for (long to_read = (long)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer/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 (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); } } } break; @@ -34086,12 +48570,12 @@ namespace cimg_library { *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - for (long to_read = (long)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer)); + 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 (unsigned long off = (unsigned long)raw._width/3; off; --off) { + for (ulongT off = (ulongT)raw._width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); @@ -34104,13 +48588,13 @@ namespace cimg_library { *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - for (long to_read = (int)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer/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 (unsigned long off = (unsigned long)raw._width/3; off; --off) { + for (ulongT off = (ulongT)raw._width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); @@ -34122,31 +48606,31 @@ namespace cimg_library { CImg raw; assign(W,H,D,1); T *ptrd = data(0,0,0,0); - for (long to_read = (long)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer)); + 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 (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); } } break; case 9 : { // 2d/3d grey binary with float values (PINK extension). CImg raw; assign(W,H,D,1); T *ptrd = data(0,0,0,0); - for (long to_read = (long)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer)); + 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 (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + 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.", + "load_pnm(): PNM type 'P%d' found, but type is not supported.", cimg_instance, filename?filename:"(FILE*)",ppm_type); } @@ -34154,20 +48638,25 @@ namespace cimg_library { return *this; } - //! Load an image from a PFM file. + //! Load image from a PFM file. + /** + \param filename Filename, as a C-string. + **/ CImg& load_pfm(const char *const filename) { return _load_pfm(0,filename); } + //! Load image from a PFM file \newinstance. static CImg get_load_pfm(const char *const filename) { return CImg().load_pfm(filename); } - //! Load an image from a PFM file. + //! Load image from a PFM file \overloading. CImg& load_pfm(std::FILE *const file) { return _load_pfm(file,0); } + //! Load image from a PFM file \newinstance. static CImg get_load_pfm(std::FILE *const file) { return CImg().load_pfm(file); } @@ -34175,41 +48664,42 @@ namespace cimg_library { CImg& _load_pfm(std::FILE *const file, const char *const filename) { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_pfm() : Specified filename is (null).", + "load_pfm(): Specified filename is (null).", cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - char pfm_type, item[1024] = { 0 }; + char pfm_type; + CImg item(16384,1,1,1,0); int W = 0, H = 0, err = 0; double scale = 0; - while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item," P%c",&pfm_type)!=1) { + 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'.", + "load_pfm(): PFM header not found in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } - while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if ((err=std::sscanf(item," %d %d",&W,&H))<2) { + 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'.", + "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } if (err==2) { - while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item,"%lf",&scale)!=1) + 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'.", + "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,0); + assign(W,H,1,3,(T)0); CImg 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) { @@ -34223,7 +48713,7 @@ namespace cimg_library { } } } else { - assign(W,H,1,1,0); + assign(W,H,1,1,(T)0); CImg buf(W); T *ptrd = data(0,0,0,0); cimg_forY(*this,y) { @@ -34234,35 +48724,43 @@ namespace cimg_library { } } if (!file) cimg::fclose(nfile); - return *this; + return mirror('y'); // Most of the .pfm files are flipped along the y-axis. } - //! Load an image from a RGB file. + //! 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& 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 get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgb(filename,dimw,dimh); } - //! Load an image from a RGB file. + //! Load image from a RGB file \overloading. CImg& 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 get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgb(file,dimw,dimh); } - CImg& _load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + CImg& _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).", + "load_rgb(): Specified filename is (null).", cimg_instance); if (!dimw || !dimh) return assign(); - const long cimg_iobuffer = 12*1024*1024; + const longT cimg_iobuffer = (longT)24*1024*1024; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg raw; assign(dimw,dimh,1,3); @@ -34270,12 +48768,12 @@ namespace cimg_library { *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - for (long to_read = (long)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer)); + 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 (unsigned long off = raw._width/3UL; off; --off) { + for (ulongT off = raw._width/3UL; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); @@ -34285,32 +48783,40 @@ namespace cimg_library { return *this; } - //! Load an image from a RGBA file. + //! 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& 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 get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgba(filename,dimw,dimh); } - //! Load an image from a RGBA file. + //! Load image from a RGBA file \overloading. CImg& 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 get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { return CImg().load_rgba(file,dimw,dimh); } - CImg& _load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + CImg& _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).", + "load_rgba(): Specified filename is (null).", cimg_instance); if (!dimw || !dimh) return assign(); - const long cimg_iobuffer = 12*1024*1024; + const longT cimg_iobuffer = (longT)24*1024*1024; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg raw; assign(dimw,dimh,1,4); @@ -34319,12 +48825,12 @@ namespace cimg_library { *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); - for (long to_read = (long)size(); to_read>0; ) { - raw.assign(cimg::min(to_read,cimg_iobuffer)); + 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 (unsigned long off = raw._width/4UL; off; --off) { + for (ulongT off = raw._width/4UL; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); @@ -34335,27 +48841,31 @@ namespace cimg_library { return *this; } - //! Load an image from a TIFF file. + //! 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 cimg_use_tif. - + directive \c cimg_use_tif. - When libtiff is enabled, 2D and 3D (multipage) several channel per pixel are supported for - char,uchar,short,ushort,float and double pixel type. - - - If cimg_use_tif is not defined at compilation time the - function uses CImg&load_other(const char*). - - \see CImg& load_other(const char*) - \see CImg& save_tiff(const char*, const unsigned int) + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tif is not defined at compile time the + function uses CImg& load_other(const char*). **/ CImg& load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1) { + const unsigned int step_frame=1, + float *const voxel_size=0, + CImg *const description=0) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_tiff() : Specified filename is (null).", + "load_tiff(): Specified filename is (null).", cimg_instance); const unsigned int @@ -34364,76 +48874,89 @@ namespace cimg_library { unsigned int nlast_frame = first_frame1) throw CImgArgumentException(_cimg_instance - "load_tiff() : Unable to read sub-images from file '%s' unless libtiff is enabled.", + "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).", + "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; + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; TIFFSetDirectory(tif,0); CImg frame; for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { - frame._load_tiff(tif,l); - if (l==nfirst_frame) assign(frame._width,frame._height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame._spectrum); + 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(cimg::max(frame._width,_width),cimg::max(frame._height,_height),-100,cimg::max(frame._spectrum,_spectrum),0); - draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame); + 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 CImgException(_cimg_instance - "load_tiff() : Failed to open file '%s'.", - cimg_instance, - filename); + } 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 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) { - return CImg().load_tiff(filename,first_frame,last_frame,step_frame); + const unsigned int step_frame=1, + float *const voxel_size=0, + CImg *const description=0) { + return CImg().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); } // (Original contribution by Jerome Boulanger). #ifdef cimg_use_tiff template - void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + 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 - void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + 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; vvny?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 CImgException(_cimg_instance - "load_tiff() : Invalid strip in file '%s'.", - cimg_instance, - TIFFFileName(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; rrny?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 CImgException(_cimg_instance - "load_tiff() : Invalid strip in file '%s'.", - cimg_instance, - TIFFFileName(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& _load_tiff(TIFF *const tif, const unsigned int directory) { + CImg& _load_tiff(TIFF *const tif, const unsigned int directory, + float *const voxel_size, CImg *const description) { if (!TIFFSetDirectory(tif,directory)) return assign(); - uint16 samplesperpixel, bitspersample; - uint16 sampleformat = SAMPLEFORMAT_UINT; - uint32 nx,ny; + 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_SAMPLESPERPIXEL,&samplesperpixel); TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); - assign(nx,ny,1,samplesperpixel); - if (bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4)) { - uint16 photo, config; + 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.0f/voxel_size[0]; + voxel_size[1] = 1.0f/voxel_size[1]; + } + if (description) { + const char *s_description = 0; + if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) + CImg::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); - TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); if (TIFFIsTiled(tif)) { - uint32 tw, th; + 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(tif,samplesperpixel,nx,ny,tw,th); + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); - } break; + break; case 16 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); break; case 32 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); - else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; } else switch (bitspersample) { case 8 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); break; case 16 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); break; case 32 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); - else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; } } else { if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { case 8 : - if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_contig(tif,samplesperpixel,nx,ny); else _load_tiff_contig(tif,samplesperpixel,nx,ny); break; case 16 : @@ -34570,7 +49178,12 @@ namespace cimg_library { else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); else _load_tiff_contig(tif,samplesperpixel,nx,ny); break; - } else switch (bitspersample){ + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + } else switch (bitspersample) { case 8 : if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); else _load_tiff_separate(tif,samplesperpixel,nx,ny); @@ -34584,178 +49197,102 @@ namespace cimg_library { else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); else _load_tiff_separate(tif,samplesperpixel,nx,ny); break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; } } - } else { - 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 (samplesperpixel) { - case 1 : { - cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x] + 128)/257); - } 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); } return *this; } #endif - //! Load an image from a MINC2 file. + //! Load image from a MINC2 file. + /** + \param filename Filename, as a C-string. + **/ // (Original code by Haz-Edine Assemlal). CImg& load_minc2(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_minc2() : Specified filename is (null).", + "load_minc2(): Specified filename is (null).", cimg_instance); #ifndef cimg_use_minc2 return load_other(filename); #else - - // Open the MINC2 volume in read-only access. - mihandle_t hvol; - int result = miopen_volume(filename,MI2_OPEN_RDWR,&hvol); - if (result!=MI_NOERROR) - throw CImgIOException(_cimg_instance - "load_minc2() : Invalid MINC2 format for file '%s'.", - cimg_instance,filename); - - mitype_t volume_data_type; - result = miget_data_type(hvol,&volume_data_type); - - int slice_scaling_flag; - result = miget_slice_scaling_flag(hvol,&slice_scaling_flag); - - double valid_max, valid_min; - result = miget_volume_valid_range(hvol,&valid_max,&valid_min); - - miclass_t volume_data_class; - result = miget_data_class(hvol,&volume_data_class); - - midimhandle_t *const hdims = (midimhandle_t*)std::malloc(4*sizeof(midimhandle_t)); - result = miget_volume_dimensions(hvol,MI_DIMCLASS_ANY,MI_DIMATTR_ALL,MI_DIMORDER_FILE,4,hdims); - - CImg minc_dims(4,1,1,1,0); - cimg_forX(minc_dims,d) { - miboolean_t irregular; - result = miget_dimension_sampling_flag(hdims[d],&irregular); - if (irregular) { - char* name; - result = miget_dimension_name(hdims[d],&name); - const CImg _name = CImg::string(name); - mifree_name(name); - throw CImgIOException(_cimg_instance - "load_minc2() : Unsupported dimension (%s) detected in file '%s'.", - cimg_instance,name,filename); - } - } - - result = miget_dimension_sizes(hdims,4,minc_dims.data()); - miflipping_t file_order; - miflipping_t sign; - result = miget_dimension_apparent_voxel_order(*hdims,&file_order,&sign); - if (file_order) - cimg::warn(_cimg_instance - "load_minc2() : Unsupported voxel order (%d) detected in file '%s'.", - cimg_instance,file_order,filename); - - CImg permutations, permutations_inv; - const CImg - minc_dims_sort = minc_dims.get_sort(permutations,true), - minc_dims_sort_inv = minc_dims.get_sort(permutations_inv,false); - - int nb_useful_dims = 0; - if (minc_dims(0)!=0) ++nb_useful_dims; - if (minc_dims(1)!=0) ++nb_useful_dims; - if (minc_dims(2)!=0) ++nb_useful_dims; - if (minc_dims(3)!=0) ++nb_useful_dims; - - // BUG in MINC2 ? count as to be equal to 4, not to nb_useful_dims. - CImg count(4,1,1,1,1); - unsigned int c = 0; - cimg_foroff(minc_dims,d) if (minc_dims(d)) count(c++) = minc_dims(d); - assign(!minc_dims(0)?1:minc_dims(0), - !minc_dims(1)?1:minc_dims(1), - !minc_dims(2)?1:minc_dims(2), - !minc_dims(3)?1:minc_dims(3)); - if (nb_useful_dims==2) permute_axes("yxzc"); - else if (nb_useful_dims==3) permute_axes("zyxc"); - else if (nb_useful_dims==4) permute_axes("czyx"); - - // Read the entire file in one operation. - CImg start(4,1,1,1,0); - if (slice_scaling_flag) result = miget_real_value_hyperslab(hvol,MI_TYPE_FLOAT,start.data(),count.data(),data()); - else result = miget_voxel_value_hyperslab(hvol,MI_TYPE_FLOAT,start.data(),count.data(),data()); - - // Close the MINC2 volume. - result = mifree_dimension_handle(*hdims); - miclose_volume(hvol); - return mirror('y'); + 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 (typeid(T)==typeid(unsigned char)) + rdr.setup_read_byte(); + else if (typeid(T)==typeid(int)) + rdr.setup_read_int(); + else if (typeid(T)==typeid(double)) + rdr.setup_read_double(); + else + rdr.setup_read_float(); + minc::load_standard_volume(rdr, this->_data); + return *this; #endif } - //! Load an image from an ANALYZE7.5/NIFTI file. - CImg& load_analyze(const char *const filename, float *const voxsize=0) { - return _load_analyze(0,filename,voxsize); + //! Load image from a MINC2 file \newinstance. + static CImg get_load_minc2(const char *const filename) { + return CImg().load_analyze(filename); } - static CImg get_load_analyze(const char *const filename, float *const voxsize=0) { - return CImg().load_analyze(filename,voxsize); + //! 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& load_analyze(const char *const filename, float *const voxel_size=0) { + return _load_analyze(0,filename,voxel_size); } - //! Load an image from an ANALYZE7.5/NIFTI file. - CImg& load_analyze(std::FILE *const file, float *const voxsize=0) { - return _load_analyze(file,0,voxsize); + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(const char *const filename, float *const voxel_size=0) { + return CImg().load_analyze(filename,voxel_size); } - static CImg get_load_analyze(std::FILE *const file, float *const voxsize=0) { - return CImg().load_analyze(file,voxsize); + //! Load image from an ANALYZE7.5/NIFTI file \overloading. + CImg& load_analyze(std::FILE *const file, float *const voxel_size=0) { + return _load_analyze(file,0,voxel_size); } - CImg& _load_analyze(std::FILE *const file, const char *const filename, float *const voxsize=0) { + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(std::FILE *const file, float *const voxel_size=0) { + return CImg().load_analyze(file,voxel_size); + } + + CImg& _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).", + "load_analyze(): Specified filename is (null).", cimg_instance); std::FILE *nfile_header = 0, *nfile = 0; if (!file) { - char body[1024] = { 0 }; + CImg 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"); - std::sprintf(body + std::strlen(body),".img"); + 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"); - std::sprintf(body + std::strlen(body),".hdr"); + 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'.", + "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); @@ -34765,31 +49302,38 @@ namespace cimg_library { cimg::fread(&header_size,1,nfile_header); if (!header_size) throw CImgIOException(_cimg_instance - "load_analyze() : Invalid zero-sized header in file '%s'.", + "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); + 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+112),1); + 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); } - unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 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.", + "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.", + "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.", cimg_instance, filename?filename:"(FILE*)",dim[0]); @@ -34797,55 +49341,56 @@ namespace cimg_library { 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 = *(short*)(header+70); - if (voxsize) { - const float *vsize = (float*)(header+76); - voxsize[0] = vsize[1]; voxsize[1] = vsize[2]; voxsize[2] = vsize[3]; + 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[dimx*dimy*dimz*dimv]; - cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); + 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[dimx*dimy*dimz*dimv]; - cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); - if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + 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[dimx*dimy*dimz*dimv]; - cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); - if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + 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[dimx*dimy*dimz*dimv]; - cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); - if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + 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[dimx*dimy*dimz*dimv]; - cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); - if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + 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'", + "load_analyze(): Unable to load datatype %d in file '%s'", cimg_instance, datatype,filename?filename:"(FILE*)"); } @@ -34853,7 +49398,12 @@ namespace cimg_library { return *this; } - //! Load an image (list) from a .cimg file. + //! 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 { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ CImg& load_cimg(const char *const filename, const char axis='z', const float align=0) { CImgList list; list.load_cimg(filename); @@ -34861,11 +49411,12 @@ namespace cimg_library { return assign(list.get_append(axis,align)); } + //! Load image from a .cimg[z] file \newinstance static CImg get_load_cimg(const char *const filename, const char axis='z', const float align=0) { return CImg().load_cimg(filename,axis,align); } - //! Load an image (list) from a .cimg file. + //! Load image from a .cimg[z] file \overloading. CImg& load_cimg(std::FILE *const file, const char axis='z', const float align=0) { CImgList list; list.load_cimg(file); @@ -34873,15 +49424,33 @@ namespace cimg_library { return assign(list.get_append(axis,align)); } + //! Load image from a .cimg[z] file \newinstance static CImg get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) { return CImg().load_cimg(file,axis,align); } - //! Load a sub-image (list) from a .cimg file. + //! 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 { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ CImg& 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 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 list; list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); @@ -34889,19 +49458,24 @@ namespace cimg_library { return assign(list.get_append(axis,align)); } + //! Load sub-images of a .cimg file \newinstance. static CImg 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 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().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); } - //! Load a sub-image (list) from a non-compressed .cimg file. + //! Load sub-images of a .cimg file \overloading. CImg& 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 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 list; list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); @@ -34909,86 +49483,95 @@ namespace cimg_library { return assign(list.get_append(axis,align)); } + //! Load sub-images of a .cimg file \newinstance. static CImg 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 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().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); } - //! Load an image from an INRIMAGE-4 file. - CImg& load_inr(const char *const filename, float *const voxsize=0) { - return _load_inr(0,filename,voxsize); + //! 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& load_inr(const char *const filename, float *const voxel_size=0) { + return _load_inr(0,filename,voxel_size); } - static CImg get_load_inr(const char *const filename, float *const voxsize=0) { - return CImg().load_inr(filename,voxsize); + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(const char *const filename, float *const voxel_size=0) { + return CImg().load_inr(filename,voxel_size); } - //! Load an image from an INRIMAGE-4 file. - CImg& load_inr(std::FILE *const file, float *const voxsize=0) { - return _load_inr(file,0,voxsize); + //! Load image from an INRIMAGE-4 file \overloading. + CImg& load_inr(std::FILE *const file, float *const voxel_size=0) { + return _load_inr(file,0,voxel_size); } - static CImg get_load_inr(std::FILE *const file, float *voxsize=0) { - return CImg().load_inr(file,voxsize); + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(std::FILE *const file, float *voxel_size=0) { + return CImg().load_inr(file,voxel_size); } - // Load an image from an INRIMAGE-4 file (internal). - static void _load_inr_header(std::FILE *file, int out[8], float *const voxsize) { - char item[1024] = { 0 }, tmp1[64] = { 0 }, tmp2[64] = { 0 }; - out[0] = std::fscanf(file,"%63s",item); + static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { + CImg 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.", + 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)!=EOF && std::strncmp(item,"##}",3)) { - std::sscanf(item," XDIM%*[^0-9]%d",out); - std::sscanf(item," YDIM%*[^0-9]%d",out+1); - std::sscanf(item," ZDIM%*[^0-9]%d",out+2); - std::sscanf(item," VDIM%*[^0-9]%d",out+3); - std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6); - if (voxsize) { - std::sscanf(item," VX%*[^0-9.+-]%f",voxsize); - std::sscanf(item," VY%*[^0-9.+-]%f",voxsize+1); - std::sscanf(item," VZ%*[^0-9.+-]%f",voxsize+2); + 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 (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1; - switch (std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) { + 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,sizeof(tmp1)-1); + case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,tmp1._width - 1); case 1 : - if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; + 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 (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; if (out[4]>=0) break; default : - throw CImgIOException("CImg<%s>::load_inr() : Invalid pixel type '%s' defined in header.", + throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", pixel_type(), - tmp2); + 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.", + 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.", + 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.", + 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.", + if (out[7]<0) + throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.", pixel_type()); } - CImg& _load_inr(std::FILE *const file, const char *const filename, float *const voxsize) { + CImg& _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[fopt[0]*fopt[3]]; \ + 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]); \ @@ -35000,14 +49583,14 @@ namespace cimg_library { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_inr() : Specified filename is (null).", + "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; + int fopt[8], endian = cimg::endianness()?1:0; bool loaded = false; - if (voxsize) voxsize[0]=voxsize[1]=voxsize[2]=1; - _load_inr_header(nfile,fopt,voxsize); + 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); @@ -35022,7 +49605,7 @@ namespace cimg_library { if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimg_instance - "load_inr() : Unknown pixel type defined in file '%s'.", + "load_inr(): Unknown pixel type defined in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } @@ -35030,16 +49613,16 @@ namespace cimg_library { return *this; } - //! Load an image from a EXR file. + //! Load image from a EXR file. + /** + \param filename Filename, as a C-string. + **/ CImg& load_exr(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_exr() : Specified filename is (null).", + "load_exr(): Specified filename is (null).", cimg_instance); - -#ifndef cimg_use_openexr - return load_other(filename); -#else +#if defined(cimg_use_openexr) Imf::RgbaInputFile file(filename); Imath::Box2i dw = file.dataWindow(); const int @@ -35057,28 +49640,46 @@ namespace cimg_library { *(ptr_b++) = (T)pixels[y][x].b; *(ptr_a++) = (T)pixels[y][x].a; } - return *this; +#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(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 get_load_exr(const char *const filename) { return CImg().load_exr(filename); } - //! Load an image from a PANDORE file. + //! Load image from a PANDORE-5 file. + /** + \param filename Filename, as a C-string. + **/ CImg& load_pandore(const char *const filename) { return _load_pandore(0,filename); } + //! Load image from a PANDORE-5 file \newinstance. static CImg get_load_pandore(const char *const filename) { return CImg().load_pandore(filename); } - //! Load an image from a PANDORE file. + //! Load image from a PANDORE-5 file \overloading. CImg& load_pandore(std::FILE *const file) { return _load_pandore(file,0); } + //! Load image from a PANDORE-5 file \newinstance. static CImg get_load_pandore(std::FILE *const file) { return CImg().load_pandore(file); } @@ -35088,7 +49689,7 @@ namespace cimg_library { cimg::fread(dims,nbdim,nfile); \ if (endian) cimg::invert_endianness(dims,nbdim); \ assign(nwidth,nheight,ndepth,ndim); \ - const unsigned int siz = size(); \ + const size_t siz = size(); \ stype *buffer = new stype[siz]; \ cimg::fread(buffer,siz,nfile); \ if (endian) cimg::invert_endianness(buffer,siz); \ @@ -35102,41 +49703,42 @@ namespace cimg_library { 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'.", \ + "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).", + "load_pandore(): Specified filename is (null).", cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - char header[32] = { 0 }; - cimg::fread(header,12,nfile); + CImg 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'.", + "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); + const bool endian = imageid>255; if (endian) cimg::invert_endianness(imageid); - cimg::fread(header,20,nfile); + 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 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); @@ -35174,7 +49776,7 @@ namespace cimg_library { cimg::fread(dims,4,nfile); if (endian) cimg::invert_endianness(dims,4); assign(dims[2],dims[1],1,1); - const unsigned int siz = size(); + const size_t siz = size(); if (dims[3]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); @@ -35192,7 +49794,7 @@ namespace cimg_library { buffer-=siz; delete[] buffer; } else { - unsigned long *buffer = new unsigned long[siz]; + unsigned int *buffer = new unsigned int[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::invert_endianness(buffer,siz); T *ptrd = _data; @@ -35207,7 +49809,7 @@ namespace cimg_library { cimg::fread(dims,5,nfile); if (endian) cimg::invert_endianness(dims,5); assign(dims[3],dims[2],dims[1],1); - const unsigned int siz = size(); + const size_t siz = size(); if (dims[4]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); @@ -35236,38 +49838,37 @@ namespace cimg_library { } } 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); - 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 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); + 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 - int ptbuf[4] = { 0 }; cimg::fread(ptbuf,1,nfile); if (endian) cimg::invert_endianness(ptbuf,1); assign(1); (*this)(0) = (T)ptbuf[0]; } break; case 35 : { // Points 2d - int ptbuf[4] = { 0 }; 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 - int ptbuf[4] = { 0 }; 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]; @@ -35275,7 +49876,7 @@ namespace cimg_library { default : if (!file) cimg::fclose(nfile); throw CImgIOException(_cimg_instance - "load_pandore() : Unable to load data with ID_type %u in file '%s'.", + "load_pandore(): Unable to load data with ID_type %u in file '%s'.", cimg_instance, imageid,filename?filename:"(FILE*)"); } @@ -35283,7 +49884,12 @@ namespace cimg_library { return *this; } - //! Load an image from a PAR-REC (Philips) file. + //! 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 { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ CImg& load_parrec(const char *const filename, const char axis='c', const float align=0) { CImgList list; list.load_parrec(filename); @@ -35291,66 +49897,99 @@ namespace cimg_library { return assign(list.get_append(axis,align)); } + //! Load image from a PAR-REC (Philips) file \newinstance. static CImg get_load_parrec(const char *const filename, const char axis='c', const float align=0) { return CImg().load_parrec(filename,axis,align); } - //! Load an image from a .RAW file. + //! 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& load_raw(const char *const filename, - const unsigned int sizex=0, const unsigned int sizey=1, - const unsigned int sizez=1, const unsigned int sizev=1, - const bool multiplexed=false, const bool invert_endianness=false) { - return _load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + 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 get_load_raw(const char *const filename, - const unsigned int sizex=0, const unsigned int sizey=1, - const unsigned int sizez=1, const unsigned int sizev=1, - const bool multiplexed=false, const bool invert_endianness=false) { - return CImg().load_raw(filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + 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().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); } - //! Load an image from a .RAW file. + //! Load image from a raw binary file \overloading. CImg& load_raw(std::FILE *const file, - const unsigned int sizex=0, const unsigned int sizey=1, - const unsigned int sizez=1, const unsigned int sizev=1, - const bool multiplexed=false, const bool invert_endianness=false) { - return _load_raw(file,0,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + 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 get_load_raw(std::FILE *const file, - const unsigned int sizex=0, const unsigned int sizey=1, - const unsigned int sizez=1, const unsigned int sizev=1, - const bool multiplexed=false, const bool invert_endianness=false) { - return CImg().load_raw(file,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + 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().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); } CImg& _load_raw(std::FILE *const file, const char *const filename, - const unsigned int sizex, const unsigned int sizey, - const unsigned int sizez, const unsigned int sizev, - const bool multiplexed, const bool invert_endianness) { + 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).", + "load_raw(): Specified filename is (null).", cimg_instance); - unsigned int siz = sizex*sizey*sizez*sizev, _sizex = sizex, _sizey = sizey, _sizez = sizez, _sizev = sizev; + 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 long fpos = std::ftell(nfile); - std::fseek(nfile,0,SEEK_END); - siz = _sizey = (unsigned int)std::ftell(nfile)/sizeof(T); - _sizex = _sizez = _sizev = 1; - std::fseek(nfile,fpos,SEEK_SET); + 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); } - assign(_sizex,_sizey,_sizez,_sizev,0); - if (!multiplexed || sizev==1) { + 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 { - CImg buf(1,1,1,_sizev); + } else if (siz) { + CImg buf(1,1,1,_size_c); cimg_forXYZ(*this,x,y,z) { - cimg::fread(buf._data,_sizev,nfile); - if (invert_endianness) cimg::invert_endianness(buf._data,_sizev); + cimg::fread(buf._data,_size_c,nfile); + if (invert_endianness) cimg::invert_endianness(buf._data,_size_c); set_vector_at(buf,x,y,z); } } @@ -35358,98 +49997,104 @@ namespace cimg_library { return *this; } - //! Load a video sequence using FFMPEG av's libraries. - CImg& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, - const char axis='z', const float align=0) { - return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).move_to(*this); - } - - static CImg get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, - const char axis='z', const float align=0) { - return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align); - } - - //! Load an image sequence from a YUV file. + //! 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 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 { 'x' | 'y' | 'z' | 'c' }. + **/ CImg& load_yuv(const char *const filename, - const unsigned int sizex, const unsigned int sizey=1, + const unsigned int size_x, const unsigned int size_y=1, 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', const float align=0) { - return get_load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).move_to(*this); + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); } + //! Load image sequence from a YUV file \newinstance. static CImg get_load_yuv(const char *const filename, - const unsigned int sizex, const unsigned int sizey=1, + const unsigned int size_x, const unsigned int size_y=1, 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', const float align=0) { - return CImgList().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align); + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); } - //! Load an image sequence from a YUV file. + //! Load image sequence from a YUV file \overloading. CImg& load_yuv(std::FILE *const file, - const unsigned int sizex, const unsigned int sizey=1, + const unsigned int size_x, const unsigned int size_y=1, 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', const float align=0) { - return get_load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).move_to(*this); + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); } + //! Load image sequence from a YUV file \newinstance. static CImg get_load_yuv(std::FILE *const file, - const unsigned int sizex, const unsigned int sizey=1, + const unsigned int size_x, const unsigned int size_y=1, 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', const float align=0) { - return CImgList().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align); + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); } - //! Load a 3d object from a .OFF file. + //! 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 - CImg& load_off(const char *const filename, CImgList& primitives, CImgList& colors) { - return _load_off(0,filename,primitives,colors); + CImg& load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return _load_off(primitives,colors,0,filename); + } + + //! Load 3d object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return CImg().load_off(primitives,colors,filename); + } + + //! Load 3d object from a .OFF file \overloading. + template + CImg& load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return _load_off(primitives,colors,file,0); + } + + //! Load 3d object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return CImg().load_off(primitives,colors,file); } template - static CImg get_load_off(const char *const filename, CImgList& primitives, CImgList& colors) { - return CImg().load_off(filename,primitives,colors); - } - - //! Load a 3d object from a .OFF file. - template - CImg& load_off(std::FILE *const file, CImgList& primitives, CImgList& colors) { - return _load_off(file,0,primitives,colors); - } - - template - static CImg get_load_off(std::FILE *const file, CImgList& primitives, CImgList& colors) { - return CImg().load_off(file,primitives,colors); - } - - template - CImg& _load_off(std::FILE *const file, const char *const filename, - CImgList& primitives, CImgList& colors) { + CImg& _load_off(CImgList& primitives, CImgList& colors, + std::FILE *const file, const char *const filename) { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "load_off() : Specified filename is (null).", + "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; - char line[256] = { 0 }; + CImg line(256); *line = 0; int err; // Skip comments, and read magic string OFF - do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#')); + 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'.", + "load_off(): OFF header not found in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } - do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#')); - if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { + 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'.", + "load_off(): Invalid number of vertices or primitives specified in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); } @@ -35458,13 +50103,13 @@ namespace cimg_library { assign(nb_points,3); float X = 0, Y = 0, Z = 0; cimg_forX(*this,l) { - do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#')); - if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { + 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'.", + "load_off(): Failed to read vertex %u/%u in file '%s'.", cimg_instance, - l+1,nb_points,filename?filename:"(FILE*)"); + l + 1,nb_points,filename?filename:"(FILE*)"); } (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; } @@ -35472,81 +50117,81 @@ namespace cimg_library { // Read primitive data primitives.assign(); colors.assign(); - bool stopflag = false; - while (!stopflag) { + 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) stopflag=true; + 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))<2) { + 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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0).move_to(primitives); CImg::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))<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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0,i1).move_to(primitives); CImg::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))<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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0,i2,i1).move_to(primitives); CImg::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))<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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0,i3,i2,i1).move_to(primitives); CImg::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))<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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0,i3,i2,i1).move_to(primitives); CImg::vector(i0,i4,i3).move_to(primitives); colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); @@ -35554,15 +50199,15 @@ namespace cimg_library { } } break; case 6 : { - if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0,i3,i2,i1).move_to(primitives); CImg::vector(i0,i5,i4,i3).move_to(primitives); colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); @@ -35570,15 +50215,15 @@ namespace cimg_library { } } 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))<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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0,i4,i3,i1).move_to(primitives); CImg::vector(i0,i6,i5,i4).move_to(primitives); CImg::vector(i3,i2,i1).move_to(primitives); @@ -35587,15 +50232,15 @@ namespace cimg_library { } } 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))<7) { + 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'.", + "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 = std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); CImg::vector(i0,i3,i2,i1).move_to(primitives); CImg::vector(i0,i5,i4,i3).move_to(primitives); CImg::vector(i0,i7,i6,i5).move_to(primitives); @@ -35605,7 +50250,7 @@ namespace cimg_library { } break; default : cimg::warn(_cimg_instance - "load_off() : Failed to read primitive %u/%u (%u vertices) from file '%s'.", + "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.", cimg_instance, nb_read,nb_primitives,prim,filename?filename:"(FILE*)"); @@ -35616,38 +50261,93 @@ namespace cimg_library { if (!file) cimg::fclose(nfile); if (primitives._width!=nb_primitives) cimg::warn(_cimg_instance - "load_off() : Only %u/%u primitives read from file '%s'.", + "load_off(): Only %u/%u primitives read from file '%s'.", cimg_instance, primitives._width,nb_primitives,filename?filename:"(FILE*)"); return *this; } - //! Load a video sequence using FFMPEG's external tool 'ffmpeg'. + //! 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& 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 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().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 { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ CImg& 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 get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) { return CImgList().load_ffmpeg_external(filename).get_append(axis,align); } - //! Load an image using GraphicsMagick's external tool 'gm'. + //! 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 { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& 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 get_load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return CImgList().load_gif_external(filename).get_append(axis,align); + } + + //! Load image using GraphicsMagick's external tool 'gm'. + /** + \param filename Filename, as a C-string. + **/ CImg& load_graphicsmagick_external(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_graphicsmagick_external() : Specified filename is (null).", + "load_graphicsmagick_external(): Specified filename is (null).", cimg_instance); - - char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + CImg command(1024), filename_tmp(256); std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); #if cimg_OS==1 - cimg_snprintf(command,sizeof(command),"%s convert \"%s\" pnm:-",cimg::graphicsmagick_path(),filename); + 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'.", + "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", cimg_instance, filename); } @@ -35656,36 +50356,43 @@ namespace cimg_library { } #endif do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s\"",cimg::graphicsmagick_path(),filename,filetmp); + cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s\"", + cimg::graphicsmagick_path(),s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); cimg::system(command,cimg::graphicsmagick_path()); - if (!(file = std::fopen(filetmp,"rb"))) { + if (!(file = 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'.", + "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", cimg_instance, filename); } else cimg::fclose(file); - load_pnm(filetmp); - std::remove(filetmp); + load_pnm(filename_tmp); + std::remove(filename_tmp); return *this; } + //! Load image using GraphicsMagick's external tool 'gm' \newinstance. static CImg get_load_graphicsmagick_external(const char *const filename) { return CImg().load_graphicsmagick_external(filename); } - //! Load a gzipped image file, using external tool 'gunzip'. + //! Load gzipped image file, using external tool 'gunzip'. + /** + \param filename Filename, as a C-string. + **/ CImg& load_gzip_external(const char *const filename) { if (!filename) throw CImgIOException(_cimg_instance - "load_gzip_external() : Specified filename is (null).", + "load_gzip_external(): Specified filename is (null).", cimg_instance); - - char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + CImg command(1024), filename_tmp(256), body(256); const char *const ext = cimg::split_filename(filename,body), *const ext2 = cimg::split_filename(body,0); @@ -35693,50 +50400,69 @@ namespace cimg_library { std::FILE *file = 0; do { if (!cimg::strcasecmp(ext,"gz")) { - if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - - cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp); + cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); cimg::system(command); - if (!(file = std::fopen(filetmp,"rb"))) { + if (!(file = 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'.", + "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.", cimg_instance, filename); } else cimg::fclose(file); - load(filetmp); - std::remove(filetmp); + load(filename_tmp); + std::remove(filename_tmp); return *this; } + //! Load gzipped image file, using external tool 'gunzip' \newinstance. static CImg get_load_gzip_external(const char *const filename) { return CImg().load_gzip_external(filename); } - //! Load an image using ImageMagick's external tool 'convert'. + //! Load image using ImageMagick's external tool 'convert'. + /** + \param filename Filename, as a C-string. + **/ CImg& load_imagemagick_external(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_imagemagick_external() : Specified filename is (null).", + "load_imagemagick_external(): Specified filename is (null).", cimg_instance); - char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + CImg command(1024), filename_tmp(256); std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); #if cimg_OS==1 - cimg_snprintf(command,sizeof(command),"%s \"%s\" pnm:-",cimg::imagemagick_path(),filename); + 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 'convert'.", + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", cimg_instance, filename); } @@ -35745,54 +50471,66 @@ namespace cimg_library { } #endif do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - cimg_snprintf(command,sizeof(command),"%s \"%s\" \"%s\"",cimg::imagemagick_path(),filename,filetmp); + 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::string(filename_tmp)._system_strescape().data()); cimg::system(command,cimg::imagemagick_path()); - if (!(file = std::fopen(filetmp,"rb"))) { + if (!(file = 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 'convert'.", + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", cimg_instance, filename); } else cimg::fclose(file); - load_pnm(filetmp); - std::remove(filetmp); + load_pnm(filename_tmp); + std::remove(filename_tmp); return *this; } + //! Load image using ImageMagick's external tool 'convert' \newinstance. static CImg get_load_imagemagick_external(const char *const filename) { return CImg().load_imagemagick_external(filename); } - //! Load a DICOM image file, using XMedcon's external tool 'medcon'. + //! Load image from a DICOM file, using XMedcon's external tool 'medcon'. + /** + \param filename Filename, as a C-string. + **/ CImg& load_medcon_external(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_medcon_external() : Specified filename is (null).", + "load_medcon_external(): Specified filename is (null).", cimg_instance); - - char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + CImg command(1024), filename_tmp(256), body(256); cimg::fclose(cimg::fopen(filename,"r")); std::FILE *file = 0; do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand()); - if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - cimg_snprintf(command,sizeof(command),"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename); + cimg_snprintf(command,command._width,"%s -w -c anlz -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); cimg::system(command); - cimg::split_filename(filetmp,body); + cimg::split_filename(filename_tmp,body); - cimg_snprintf(command,sizeof(command),"%s.hdr",body); - file = std::fopen(command,"rb"); + cimg_snprintf(command,command._width,"%s.hdr",body._data); + file = std_fopen(command,"rb"); if (!file) { - cimg_snprintf(command,sizeof(command),"m000-%s.hdr",body); - file = std::fopen(command,"rb"); + cimg_snprintf(command,command._width,"m000-%s.hdr",body._data); + file = std_fopen(command,"rb"); if (!file) { throw CImgIOException(_cimg_instance - "load_medcon_external() : Failed to load file '%s' with external command 'medcon'.", + "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.", cimg_instance, filename); } @@ -35801,32 +50539,41 @@ namespace cimg_library { load_analyze(command); std::remove(command); cimg::split_filename(command,body); - cimg_snprintf(command,sizeof(command),"%s.img",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 get_load_medcon_external(const char *const filename) { return CImg().load_medcon_external(filename); } - //! Load a RAW Color Camera image file, using external tool 'dcraw'. + //! Load image from a RAW Color Camera file, using external tool 'dcraw'. + /** + \param filename Filename, as a C-string. + **/ CImg& load_dcraw_external(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_dcraw_external() : Specified filename is (null).", + "load_dcraw_external(): Specified filename is (null).", cimg_instance); - - char command[1024] = { 0 }, filetmp[512] = { 0 }; + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + CImg command(1024), filename_tmp(256); std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); #if cimg_OS==1 - cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\"",cimg::dcraw_path(),filename); + 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'.", + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", cimg_instance, filename); } @@ -35835,60 +50582,84 @@ namespace cimg_library { } #endif do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.ppm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\" > %s",cimg::dcraw_path(),filename,filetmp); + cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\" > \"%s\"", + cimg::dcraw_path(),s_filename.data(),CImg::string(filename_tmp)._system_strescape().data()); cimg::system(command,cimg::dcraw_path()); - if (!(file = std::fopen(filetmp,"rb"))) { + if (!(file = 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'.", + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", cimg_instance, filename); } else cimg::fclose(file); - load_pnm(filetmp); - std::remove(filetmp); + 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 get_load_dcraw_external(const char *const filename) { return CImg().load_dcraw_external(filename); } - //! Load an image from a camera stream, using OpenCV. - CImg& load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) { + //! 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& 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 - const int ind = camera_index + 1; - if (ind<0 || ind>255) + if (camera_index>99) throw CImgArgumentException(_cimg_instance - "load_camera() : Invalid request for camera #%d.", + "load_camera(): Invalid request for camera #%u " + "(no more than 100 cameras can be managed simultaneously).", cimg_instance, camera_index); - static CvCapture *capture[256] = { 0 }; + static CvCapture *capture[100] = { 0 }; + static unsigned int capture_w[100], capture_h[100]; if (release_camera) { - if (capture[ind]) cvReleaseCapture(&(capture[ind])); - capture[ind] = 0; + 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[ind]) { - capture[ind] = cvCreateCameraCapture(camera_index); - if (!capture[ind]) { - if (camera_index>=0) - throw CImgIOException(_cimg_instance - "load_camera() : Failed to initialize camera #%d.", - cimg_instance, - camera_index); - else - throw CImgIOException(_cimg_instance - "load_camera() : Failed to initialize default camera.", - cimg_instance); + 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; iwidthStep - 3*img->width); assign(img->width,img->height,1,3); @@ -35897,31 +50668,40 @@ namespace cimg_library { 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 (unsigned int siz = img->width*img->height; siz; --siz) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); } + } 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); + 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 " + "load_camera(): This function requires the OpenCV library to run " "(macro 'cimg_use_opencv' must be defined).", cimg_instance); #endif - return *this; } - static CImg get_load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) { - return CImg().load_camera(camera_index,skip_frames,release_camera); + //! Load image from a camera stream, using OpenCV \newinstance. + static CImg 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().load_camera(camera_index,skip_frames,release_camera,capture_width,capture_height); } - //! Load an image using ImageMagick's or GraphicsMagick's executables. If failed, try to load a .cimg[z] file format. + //! Load image using various non-native ways. + /** + \param filename Filename, as a C-string. + **/ CImg& load_other(const char *const filename) { if (!filename) throw CImgArgumentException(_cimg_instance - "load_other() : Specified filename is (null).", + "load_other(): Specified filename is (null).", cimg_instance); const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; + cimg::exception_mode(0); try { load_magick(filename); } catch (CImgException&) { try { load_imagemagick_external(filename); } @@ -35930,20 +50710,29 @@ namespace cimg_library { catch (CImgException&) { try { load_cimg(filename); } catch (CImgException&) { - assign(); + try { + std::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; - if (is_empty()) - throw CImgIOException(_cimg_instance - "load_other() : Failed to load file '%s'. Format is not natively supported, and no external commands succeeded.", - cimg_instance, - filename); + cimg::exception_mode(omode); return *this; } + //! Load image using various non-native ways \newinstance. static CImg get_load_other(const char *const filename) { return CImg().load_other(filename); } @@ -35955,76 +50744,103 @@ namespace cimg_library { //@{ //--------------------------- - //! Display informations about the image on the standard error output. + //! Display information about the image data. /** - \param title Name for the considered image (optional). - \param display_stats Compute and display image statistics (optional). + \param title Name for the considered image. + \param display_stats Tells to compute and display image statistics. **/ const CImg& 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; - static CImg st; + CImg 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 unsigned int siz = size(), msiz = siz*sizeof(T), siz1 = siz-1; - const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = _width-1; - char _title[64] = { 0 }; - if (!title) cimg_snprintf(_title,sizeof(_title),"CImg<%s>",pixel_type()); + 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; - std::fprintf(cimg::output(),"%s: this = %p, size = (%u,%u,%u,%u) [%u %s], data = (%s*)%p", - title?title:_title,(void*)this,_width,_height,_depth,_spectrum, - mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), - mdisp==0?"b":(mdisp==1?"Kb":"Mb"), - pixel_type(),(void*)begin()); - if (_data) std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end()-1),_is_shared?"shared":"non-shared"); + CImg _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(),cimg::type::format(),cimg::type::format(_data[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; if (off!=7) std::fprintf(cimg::output(),"... "); } + if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); } } if (!is_empty() && display_stats) - std::fprintf(cimg::output()," ], min = %g, max = %g, mean = %g, std = %g, coords(min) = (%u,%u,%u,%u), coords(max) = (%u,%u,%u,%u).\n", - st[0],st[1],st[2],std::sqrt(st[3]),xm,ym,zm,vm,xM,yM,zM,vM); + 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 an image into a CImgDisplay window. + //! Display image into a CImgDisplay window. + /** + \param disp Display window. + **/ const CImg& display(CImgDisplay& disp) const { disp.display(*this); return *this; } - //! Display an image in a window with a title \p title, and wait a '_is_closed' or 'keyboard' event.\n - const CImg& display(CImgDisplay &disp, const bool display_info) const { - return _display(disp,0,display_info,false); + //! 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& 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 an image in a window with a title \p title, and wait a '_is_closed' or 'keyboard' event.\n - const CImg& display(const char *const title=0, const bool display_info=true) const { + //! 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& 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,false); + return _display(disp,title,display_info,XYZ,exit_on_anykey,false); } - const CImg& _display(CImgDisplay &disp, const char *const title, - const bool display_info, const bool exit_on_simpleclick) const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "display() : Empty instance.", - cimg_instance); - - unsigned int oldw = 0, oldh = 0, XYZ[3], key = 0; - int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1; + const CImg& _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(); @@ -36034,22 +50850,29 @@ namespace cimg_library { CImg zoom; for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) { if (reset_view) { - XYZ[0] = (x0 + x1)/2; XYZ[1] = (y0 + y1)/2; XYZ[2] = (z0 + z1)/2; - x0 = 0; y0 = 0; z0 = 0; x1 = _width - 1; y1 = _height-1; z1 = _depth-1; - oldw = disp.width(); oldh = disp.height(); + 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; + oldw = disp._width; oldh = disp._height; reset_view = false; } - if (!x0 && !y0 && !z0 && x1==width()-1 && y1==height()-1 && z1==depth()-1) zoom.assign(); - else zoom = get_crop(x0,y0,z0,x1,y1,z1); + 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& visu = zoom?zoom:*this; const unsigned int - dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0, - tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0); - if (!disp.is_fullscreen() && resize_disp) { + 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 unsigned int ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh, - dM = cimg::max(ttw,tth), diM = (unsigned int)cimg::max(disp.width(),disp.height()), - imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM); + dM = std::max(ttw,tth), diM = (unsigned int)std::max(disp.width(),disp.height()), + imgw = std::max(16U,ttw*diM/dM), imgh = std::max(16U,tth*diM/dM); disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false); resize_disp = false; } @@ -36059,14 +50882,32 @@ namespace cimg_library { 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; - const CImg& visu = zoom?zoom:*this; - const CImg selection = visu._get_select(disp,0,2,XYZ,x0,y0,z0,is_first_select); + + 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); + + if (!is_first_select) { + _XYZ[0] = (unsigned int)(x1 - x0)/2; + _XYZ[1] = (unsigned int)(y1 - y0)/2; + _XYZ[2] = (unsigned int)(z1 - z0)/2; + } + + disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; + const CImg selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1); + 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()) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; } - else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_right = !(go_left = disp.wheel()>0); } - else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); } + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + go_down = !(go_up = disp.wheel()>0); + } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { + go_left = !(go_right = disp.wheel()>0); + } + else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { + go_out = !(go_in = disp.wheel()>0); go_in_center = false; + } disp.set_wheel(); } @@ -36074,9 +50915,10 @@ namespace cimg_library { 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; + x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; + x0+=sx0; y0+=sy0; z0+=sz0; if (sx0==sx1 && sy0==sy1 && sz0==sz1) { - if (exit_on_simpleclick && !zoom) break; else reset_view = true; + if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true; } resize_disp = true; } else switch (key = disp.key()) { @@ -36088,18 +50930,19 @@ namespace cimg_library { case cimg::keyALTGR : #endif case cimg::keyALT : key = 0; break; - case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { // Special mode : play stack of frames + 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)); + 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(XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),XYZ[2])); - (++XYZ[2])%=visu._depth; + 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.0f; disp.set_wheel(); } @@ -36112,20 +50955,25 @@ namespace cimg_library { 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::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_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; + 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; + 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; + 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); @@ -36155,67 +51003,104 @@ namespace cimg_library { 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 = XYZ[0], Y = XYZ[1], Z = XYZ[2]; - if (mX=height()) { X = x0 + mX*(1+x1-x0)/_width; Z = z0 + (mY-_height)*(1+z1-z0)/_depth; Y = XYZ[1]; } - if (mX>=width() && mY4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; } - if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; } - if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; } + 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=height()) { + X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); Y = (int)_XYZ[1]; + } + if (mX>=width() && mY4) { 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 - deltax = (x1-x0)/8, deltay = (y1-y0)/8, deltaz = (z1-z0)/8, - ndeltax = deltax?deltax:(_width>1?1:0), - ndeltay = deltay?deltay:(_height>1?1:0), - ndeltaz = deltaz?deltaz:(_depth>1?1:0); - x0-=ndeltax; y0-=ndeltay; z0-=ndeltaz; - x1+=ndeltax; y1+=ndeltay; z1+=ndeltaz; + 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; } + 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)/5, ndelta = delta?delta:(_width>1?1:0); - if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; } + 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)/5, ndelta = delta?delta:(_width>1?1:0); + const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); if (x1+ndelta1?1:0); - if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; } + 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)/5, ndelta = delta?delta:(_height>1?1:0); + const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1); if (y1+ndelta1?1:0); - if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; } + 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)/5, ndelta = delta?delta:(_depth>1?1:0); + const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1); if (z1+ndelta const CImg& display_object3d(CImgDisplay& disp, const CImg& vertices, @@ -36224,17 +51109,18 @@ namespace cimg_library { const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); } - //! High-level interface for displaying a 3d object. + //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(const char *const title, const CImg& vertices, @@ -36243,18 +51129,19 @@ namespace cimg_library { const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); } - //! High-level interface for displaying a 3d object. + //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(CImgDisplay &disp, const CImg& vertices, @@ -36262,17 +51149,18 @@ namespace cimg_library { const CImgList& colors, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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(),centering, - render_static,render_motion,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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); } - //! High-level interface for displaying a 3d object. + //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(const char *const title, const CImg& vertices, @@ -36280,80 +51168,86 @@ namespace cimg_library { const CImgList& colors, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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(),centering, - render_static,render_motion,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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); } - //! High-level interface for displaying a 3d object. + //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(CImgDisplay &disp, const CImg& vertices, const CImgList& primitives, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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(),centering, - render_static,render_motion,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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); } - //! High-level interface for displaying a 3d object. + + //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(const char *const title, const CImg& vertices, const CImgList& primitives, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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(),centering, - render_static,render_motion,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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); } - //! High-level interface for displaying a 3d object. + //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(CImgDisplay &disp, const CImg& vertices, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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(),centering, - render_static,render_motion,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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); } - //! High-level interface for displaying a 3d object. + //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(const char *const title, const CImg& vertices, const bool centering=true, const int render_static=4, const int render_motion=1, - const bool double_sided=true, const float focale=500, - const float light_x=0, const float light_y=0, const float light_z=-5000, - const float specular_light=0.2f, const float specular_shine=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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(),centering, - render_static,render_motion,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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 @@ -36364,41 +51258,45 @@ namespace cimg_library { const to& opacities, const bool centering, const int render_static, const int render_motion, - const bool double_sided, const float focale, + const bool is_double_sided, const float focale, const float light_x, const float light_y, const float light_z, - const float specular_light, const float specular_shine, - const bool display_axes, float *const pose_matrix) const { + 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::type tpfloat; // Check input arguments if (is_empty()) { if (disp) return CImg(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,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); - else return CImg(1,2,1,1,64,128).resize(cimg_fitscreen(640,480,1),1,(colors && colors[0].size()==1)?1:3,3). + 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(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,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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); } - char error_message[1024] = { 0 }; + CImg 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); + "display_object3d(): Invalid specified 3d object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message.data()); if (vertices._width && !primitives) { CImgList nprimitives(vertices._width,1,1,1,1); - cimglist_for(nprimitives,l) nprimitives(l,0) = l; + cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l; return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, - render_static,render_motion,double_sided,focale, - light_x,light_y,light_z,specular_light,specular_shine, - display_axes,pose_matrix); + 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); + 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 @@ -36412,17 +51310,17 @@ namespace cimg_library { CImgList reverse_primitives; CImgList bbox_colors, bbox_colors2, axes_colors; unsigned int ns_width = 0, ns_height = 0; - int _double_sided = (int)double_sided; + int _is_double_sided = (int)is_double_sided; bool ndisplay_axes = display_axes; const CImg 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_line(0).max_min(xm):0, - ym = 0, yM = vertices?vertices.get_shared_line(1).max_min(ym):0, - zm = 0, zM = vertices?vertices.get_shared_line(2).max_min(zm):0; - const float delta = cimg::max(xM-xm,yM-ym,zM-zm); + 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, @@ -36457,7 +51355,7 @@ namespace cimg_library { // Init object pose if (init_pose) { const float - ratio = delta>0?(2.0f*cimg::min(disp.width(),disp.height())/(3.0f*delta)):1, + ratio = delta>0?(2.0f*std::min(disp.width(),disp.height())/(3.0f*delta)):1, dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2; if (centering) CImg(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose); @@ -36493,23 +51391,24 @@ namespace cimg_library { rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32; } - // Draw object + // 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.0f,Yoff + visu._height/2.0f,Zoff, rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale). draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale); - else visu._draw_object3d((void*)0,(!clicked && nrender_static>0)?zbuffer.fill(0):CImg::empty(), + else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg::empty(), Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, rotated_vertices,reverse_primitives?reverse_primitives:primitives, - colors,opacities,clicked?nrender_motion:nrender_static,_double_sided==1,focale, - width()/2.0f+light_x,height()/2.0f+light_y,light_z,specular_light,specular_shine, - sprite_scale); + colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale, + width()/2.0f + light_x,height()/2.0f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,sprite_scale); // Draw axes if (ndisplay_axes) { const float - n = (float)std::sqrt(1e-8 + r00*r00 + r01*r01 + r02*r02), + 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, @@ -36526,15 +51425,16 @@ namespace cimg_library { axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f; axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f; axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f; - 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)), + 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)), + 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)), + 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); @@ -36549,31 +51449,31 @@ namespace cimg_library { else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); } if (disp.button()&1) { const float - R = 0.45f*cimg::min(disp.width(),disp.height()), + 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 = (float)std::sqrt(u0*u0+v0*v0), - n1 = (float)std::sqrt(u1*u1+v1*v1), + 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(cimg::max(0,R2-nu0*nu0-nv0*nv0)), + nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)), nu1 = n1>R?(u1*R/n1):u1, nv1 = n1>R?(v1*R/n1):v1, - nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)), - u = nv0*nw1-nw0*nv1, - v = nw0*nu1-nu0*nw1, - w = nv0*nu1-nu0*nv1, - n = (float)std::sqrt(u*u+v*v+w*w), - alpha = (float)std::asin(n/R2); - (CImg::rotation_matrix(u,v,w,alpha)*pose).move_to(pose); + nw1 = (float)std::sqrt(std::max(0.0f,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::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.0f); pose*=s; sprite_scale*=s; } + if (focale>0) Zoff-=(y0 - y1)*focale/400; + else { const float s = std::exp((y0 - y1)/400.0f); pose*=s; sprite_scale*=s; } x0 = x1; y0 = y1; } if (disp.wheel()) { @@ -36581,26 +51481,29 @@ namespace cimg_library { else { const float s = std::exp(disp.wheel()/20.0f); 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()&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(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 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). + 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_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()) { @@ -36615,15 +51518,16 @@ namespace cimg_library { } if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false); else { - ns_width = (unsigned int)disp.width(); ns_height = disp.height(); + 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 (--_double_sided==-2) _double_sided = 1; - if (_double_sided>=0) reverse_primitives.assign(); + 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; @@ -36652,7 +51556,8 @@ namespace cimg_library { 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. + 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; @@ -36662,88 +51567,96 @@ namespace cimg_library { } break; case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); - (+visu).draw_text(0,0," Saving snapshot... ",foreground_color._data,background_color._data,1,13).display(disp); + (+visu).draw_text(0,0," Saving snapshot... ", + foreground_color._data,background_color._data,0.7f,13).display(disp); visu.save(filename); - visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + (+visu).draw_text(0,0," Snapshot '%s' saved. ", + foreground_color._data,background_color._data,0.7f,13,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; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.off",snap_number++); - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); - visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp); - vertices.save_off(filename,reverse_primitives?reverse_primitives:primitives,colors); - visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + (+visu).draw_text(0,0," Saving object... ", + foreground_color._data,background_color._data,0.7f,13).display(disp); + vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename); + (+visu).draw_text(0,0," Object '%s' saved. ", + foreground_color._data,background_color._data,0.7f,13,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; - char filename[32] = { 0 }; std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); - visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp); - vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).save(filename); - visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + (+visu).draw_text(0,0," Saving object... ", + foreground_color._data,background_color._data,0.7f,13).display(disp); + vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities). + save(filename); + (+visu).draw_text(0,0," Object '%s' saved. ", + foreground_color._data,background_color._data,0.7f,13,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; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.eps",snap_number++); - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); - visu.draw_text(0,0," Saving EPS snapshot... ",foreground_color._data,background_color._data,1,13).display(disp); + (+visu).draw_text(0,0," Saving EPS snapshot... ", + foreground_color._data,background_color._data,0.7f,13).display(disp); LibBoard::Board board; (+visu)._draw_object3d(&board,zbuffer.fill(0), Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, rotated_vertices,reverse_primitives?reverse_primitives:primitives, colors,opacities,clicked?nrender_motion:nrender_static, - _double_sided==1,focale, - visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine, + _is_double_sided==1,focale, + visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff, + specular_lightness,specular_shininess, sprite_scale); board.saveEPS(filename); - visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + (+visu).draw_text(0,0," Object '%s' saved. ", + foreground_color._data,background_color._data,0.7f,13,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; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.svg",snap_number++); - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); - visu.draw_text(0,0," Saving SVG snapshot... ",foreground_color._data,background_color._data,1,13).display(disp); + (+visu).draw_text(0,0," Saving SVG snapshot... ", + foreground_color._data,background_color._data,0.7f,13).display(disp); LibBoard::Board board; (+visu)._draw_object3d(&board,zbuffer.fill(0), Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff, rotated_vertices,reverse_primitives?reverse_primitives:primitives, colors,opacities,clicked?nrender_motion:nrender_static, - _double_sided==1,focale, - visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine, + _is_double_sided==1,focale, + visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff, + specular_lightness,specular_shininess, sprite_scale); board.saveSVG(filename); - visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp); + (+visu).draw_text(0,0," Object '%s' saved. ", + foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp); disp.set_key(key,false); key = 0; } break; #endif @@ -36753,6 +51666,10 @@ namespace cimg_library { 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)); @@ -36762,53 +51679,88 @@ namespace cimg_library { return *this; } - //! High-level interface for displaying a graph. + //! Display 1d graph in an interactive window. + /** + \param disp Display window. + \param plot_type Plot type. Can be { 0=points | 1=segments | 2=splines | 3=bars }. + \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& 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 { + 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& 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& _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.", + "display_graph(): Empty instance.", cimg_instance); - if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type()); - const unsigned int siz = _width*_height*_depth, old_normalization = disp.normalization(); + 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 = siz-1; } + if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; } int x0 = 0, x1 = width()*height()*depth() - 1, key = 0; - for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed(); ) { - if (reset_view) { x0 = 0; x1 = width()*height()*depth()-1; y0 = ymin; y1 = ymax; reset_view = false; } - CImg zoom(x1-x0+1,1,1,spectrum()); - cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg(data(x0,0,0,c),x1-x0+1,1,1,1,true); - - if (y0==y1) y0 = zoom.min_max(y1); + 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 zoom(x1 - x0 + 1,1,1,spectrum()); + cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg(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 selection = zoom.get_select_graph(disp,plot_type,vertex_type, labelx, - nxmin + x0*(nxmax-nxmin)/(siz-1), - nxmin + x1*(nxmax-nxmin)/(siz-1), - labely,y0,y1); + 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); + 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 = disp.key()) { - case cimg::keyHOME : reset_view = resize_disp = true; key = 0; disp.set_key(); break; + 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::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; @@ -36817,93 +51769,93 @@ namespace cimg_library { 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_out = !(go_in = disp.wheel()>0); + 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_up = !(go_down = 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 + (mx<0?0:(mx>=xsiz?xsiz:mx)); - if (x1-x0>4) { - x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8; + 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 - (my<0?0:(my>=ysiz?ysiz:my)); - y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8; + my = (mouse_y - 16)*ysiz/(disp.height() - 32), + cy = y1 - cimg::cut(my,0.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)siz-1) { - const int deltax = (x1-x0)/8, ndeltax = deltax?deltax:(siz>1?1:0); - const double ndeltay = (y1-y0)/8; - x0-=ndeltax; x1+=ndeltax; - y0-=ndeltay; y1+=ndeltay; - if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz-1; } - if (x1>=(int)siz) { x0-=(x1-siz+1); x1 = (int)siz-1; if (x0<0) x0 = 0; } + 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; } + 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+=(siz-1-x1); x1 = siz-1; } + 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; + 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; + 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; } - //! High-level interface for displaying a graph. - const CImg& 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 { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "display_graph() : Empty instance.", - cimg_instance); - CImgDisplay disp; - return display_graph(disp.set_title("%s",title),plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); - } - - //! Save the image as a file. + //! Save image as a file. /** - 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. + \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& save(const char *const filename, const int number=-1) const { + const CImg& 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).", + "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); - char nfilename[1024] = { 0 }; - const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename; + CImg 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 @@ -36931,7 +51883,7 @@ namespace cimg_library { #ifdef cimg_save_plugin8 cimg_save_plugin8(fn); #endif - // ASCII formats + // 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); @@ -36985,161 +51937,161 @@ namespace cimg_library { !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_ffmpeg(fn); + !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); return save_other(fn); } - // Save the image as an ASCII file (ASCII Raw + simple header) (internal). + //! Save image as an ascii file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_ascii(const char *const filename) const { + return _save_ascii(0,filename); + } + + //! Save image as an ascii file \overloading. + const CImg& save_ascii(std::FILE *const file) const { + return _save_ascii(file,0); + } + const CImg& _save_ascii(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_ascii() : Specified filename is (null).", + "save_ascii(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_ascii() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - 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,"%g ",(double)*(ptrs++)); + cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++)); std::fputc('\n',nfile); } if (!file) cimg::fclose(nfile); return *this; } - //! Save the image as an ASCII file (ASCII Raw + simple header). - const CImg& save_ascii(const char *const filename) const { - return _save_ascii(0,filename); + //! Save image as a .cpp source file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_cpp(const char *const filename) const { + return _save_cpp(0,filename); } - //! Save the image as an ASCII file (ASCII Raw + simple header). - const CImg& save_ascii(std::FILE *const file) const { - return _save_ascii(file,0); + //! Save image as a .cpp source file \overloading. + const CImg& save_cpp(std::FILE *const file) const { + return _save_cpp(file,0); } - // Save the image as a C or CPP source file (internal). const CImg& _save_cpp(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_cpp() : Specified filename is (null).", + "save_cpp(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_cpp() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); - std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); - char varname[1024] = { 0 }; - if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname); - if (!*varname) cimg_snprintf(varname,sizeof(varname),"unnamed"); + CImg 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[] = { \n ", - varname,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname); - for (unsigned int off = 0, siz = size()-1; off<=siz; ++off) { + "%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::format(),cimg::type::format((*this)[off])); if (off==siz) std::fprintf(nfile," };\n"); - else if (!((off+1)%16)) 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 the image as a CPP source file. - const CImg& save_cpp(const char *const filename) const { - return _save_cpp(0,filename); + //! Save image as a DLM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_dlm(const char *const filename) const { + return _save_dlm(0,filename); } - //! Save the image as a CPP source file. - const CImg& save_cpp(std::FILE *const file) const { - return _save_cpp(file,0); + //! Save image as a DLM file \overloading. + const CImg& save_dlm(std::FILE *const file) const { + return _save_dlm(file,0); } - // Save the image as a DLM file (internal). const CImg& _save_dlm(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_dlm() : Specified filename is (null).", + "save_dlm(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_dlm() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "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'.", + "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,"%g%s",(double)*(ptrs++),(x==width()-1)?"":","); + 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 the image as a DLM file. - const CImg& save_dlm(const char *const filename) const { - return _save_dlm(0,filename); + //! Save image as a BMP file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_bmp(const char *const filename) const { + return _save_bmp(0,filename); } - //! Save the image as a DLM file. - const CImg& save_dlm(std::FILE *const file) const { - return _save_dlm(file,0); + //! Save image as a BMP file \overloading. + const CImg& save_bmp(std::FILE *const file) const { + return _save_bmp(file,0); } - // Save the image as a BMP file (internal). const CImg& _save_bmp(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_bmp() : Specified filename is (null).", + "save_bmp(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_bmp() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "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'.", + "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"); - unsigned char header[54] = { 0 }, align_buf[4] = { 0 }; + CImg 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(), @@ -37169,12 +52121,12 @@ namespace cimg_library { header[0x25] = (buf_size>>24)&0xFF; header[0x27] = 0x1; header[0x2B] = 0x1; - cimg::fwrite(header,54,nfile); + 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; + *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 : { @@ -37214,47 +52166,46 @@ namespace cimg_library { return *this; } - //! Save the image as a BMP file. - const CImg& save_bmp(const char *const filename) const { - return _save_bmp(0,filename); + //! Save image as a JPEG file. + /** + \param filename Filename, as a C-string. + \param quality Image quality (in %) + **/ + const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { + return _save_jpeg(0,filename,quality); } - //! Save the image as a BMP file. - const CImg& save_bmp(std::FILE *const file) const { - return _save_bmp(file,0); + //! Save image as a JPEG file \overloading. + const CImg& save_jpeg(std::FILE *const file, const unsigned int quality=100) const { + return _save_jpeg(file,0,quality); } - // Save a file in JPEG format (internal). const CImg& _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).", + "save_jpeg(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_jpeg() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "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.", + "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) { + 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; + default : dimbuf = 4; colortype = JCS_CMYK; break; } // Call libjpeg functions @@ -37275,20 +52226,20 @@ namespace cimg_library { JSAMPROW row_pointer[1]; CImg buffer(_width*dimbuf); - while (cinfo.next_scanline < cinfo.image_height) { + while (cinfo.next_scanline& save_jpeg(const char *const filename, const unsigned int quality=100) const { - return _save_jpeg(0,filename,quality); - } - - //! Save a file in JPEG format. - const CImg& save_jpeg(std::FILE *const file, const unsigned int quality=100) const { - return _save_jpeg(file,0,quality); - } - - //! Save the image using built-in ImageMagick++ library. + //! 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& 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).", + "save_magick(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_magick() : Empty instance, for file '%s'.", - cimg_instance, - filename); + 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'.", + "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'.", + "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'.", + "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", cimg_instance, filename,stmin,stmax); @@ -37378,20 +52321,20 @@ namespace cimg_library { Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height); switch (_spectrum) { case 1 : // Scalar images - for (unsigned int off = _width*_height; off; --off) { + 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 (unsigned int off = _width*_height; off; --off) { + 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 (unsigned int off = _width*_height; off; --off) { + 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++); @@ -37400,65 +52343,83 @@ namespace cimg_library { } 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.", + "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.", cimg_instance, filename); #endif - return *this; } - // Save an image to a PNG file (internal). - // Most of this function has been written by Eric Fausett - const CImg& _save_png(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const { + //! 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& 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& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_png(file,0,bytes_per_pixel); + } + + const CImg& _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).", + "save_png(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_png() : Empty image, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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.", + "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.", cimg_instance); #else - const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'. - std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); +#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'.", + "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'.", + "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'.", + "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){ + 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'.", + "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -37467,7 +52428,7 @@ namespace cimg_library { 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'.", + "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -37475,12 +52436,14 @@ namespace cimg_library { 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'.", + "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; @@ -37495,7 +52458,7 @@ namespace cimg_library { 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); + 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]; @@ -37587,7 +52550,7 @@ namespace cimg_library { default : if (!file) cimg::fclose(nfile); throw CImgIOException(_cimg_instance - "save_png() : Encountered unknown fatal error in libpng when saving file '%s'.", + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", cimg_instance, nfilename?nfilename:"(FILE*)"); } @@ -37598,59 +52561,48 @@ namespace cimg_library { // Deallocate Image Write Memory cimg_forY(*this,n) delete[] imgData[n]; delete[] imgData; + if (!file) cimg::fclose(nfile); return *this; #endif } - //! Save a file in PNG format - const CImg& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const { - return _save_png(0,filename,bytes_per_pixel); - } - - //! Save a file in PNG format - const CImg& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { - return _save_png(file,0,bytes_per_pixel); - } - - //! Save the image as a PNM file. + //! 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& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const { return _save_pnm(0,filename,bytes_per_pixel); } - //! Save the image as a PNM file. + //! Save image as a PNM file \overloading. const CImg& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { return _save_pnm(file,0,bytes_per_pixel); } - // Save the image as a PNM file (internal function). - const CImg& _save_pnm(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const { + const CImg& _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).", + "save_pnm(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_pnm() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "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'.", + "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'.", + "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", cimg_instance, stmin,stmax,filename?filename:"(FILE*)"); @@ -37659,7 +52611,7 @@ namespace cimg_library { *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 = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3)); + 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)); @@ -37667,20 +52619,20 @@ namespace cimg_library { switch (_spectrum) { case 1 : { // Scalar image if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits - CImg buf(buf_size); - for (long to_write = _width*_height; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + CImg 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 (long i = (long)N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++); + 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 buf(buf_size); - for (long to_write = _width*_height; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + CImg 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 (long i = (long)N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++); + 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; @@ -37689,11 +52641,11 @@ namespace cimg_library { } break; case 2 : { // RG image if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits - CImg buf(buf_size); - for (long to_write = _width*_height; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + CImg 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 (long i = (long)N; i>0; --i) { + for (ulongT i = N; i>0; --i) { *(ptrd++) = (unsigned char)*(ptr_r++); *(ptrd++) = (unsigned char)*(ptr_g++); *(ptrd++) = 0; @@ -37702,11 +52654,11 @@ namespace cimg_library { to_write-=N; } } else { // Binary PPM 16 bits - CImg buf(buf_size); - for (long to_write = _width*_height; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + CImg 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 (long i = (long)N; i>0; --i) { + for (ulongT i = N; i>0; --i) { *(ptrd++) = (unsigned short)*(ptr_r++); *(ptrd++) = (unsigned short)*(ptr_g++); *(ptrd++) = 0; @@ -37719,11 +52671,11 @@ namespace cimg_library { } break; default : { // RGB image if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits - CImg buf(buf_size); - for (long to_write = _width*_height; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + CImg 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 (long i = (long)N; i>0; --i) { + for (ulongT i = N; i>0; --i) { *(ptrd++) = (unsigned char)*(ptr_r++); *(ptrd++) = (unsigned char)*(ptr_g++); *(ptrd++) = (unsigned char)*(ptr_b++); @@ -37731,12 +52683,12 @@ namespace cimg_library { cimg::fwrite(buf._data,3*N,nfile); to_write-=N; } - } else { // Binary PPM 16 bits - CImg buf(buf_size); - for (long to_write = _width*_height; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3); + } else { // Binary PPM 16 bits + CImg 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 (long i = (long)N; i>0; --i) { + for (ulongT i = N; i>0; --i) { *(ptrd++) = (unsigned short)*(ptr_r++); *(ptrd++) = (unsigned short)*(ptr_g++); *(ptrd++) = (unsigned short)*(ptr_b++); @@ -37752,67 +52704,66 @@ namespace cimg_library { return *this; } - //! Save the image as a PNK file (PINK library extension of PGM). + //! Save image as a PNK file. + /** + \param filename Filename, as a C-string. + **/ const CImg& save_pnk(const char *const filename) const { return _save_pnk(0,filename); } - //! Save the image as a PNk file (PINK library extension of PGM). + //! Save image as a PNK file \overloading. const CImg& save_pnk(std::FILE *const file) const { return _save_pnk(file,0); } - // Save the image as a PNK file (internal function). const CImg& _save_pnk(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_pnk() : Specified filename is (null).", + "save_pnk(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_pnk() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); - const unsigned long buf_size = cimg::min(1024*1024LU,_width*_height*_depth); + 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::is_float() && sizeof(T)==1 && _depth<2) _save_pnm(file,filename,0); // Can be saved as regular PNM file. - else if (!cimg::type::is_float() && sizeof(T)==1) { // Save as extended P5 file : Binary byte-valued 3d. + if (!cimg::type::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file. + _save_pnm(file,filename,0); + else if (!cimg::type::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 buf(buf_size); - for (long to_write = _width*_height*_depth; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + CImg 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 (long i = (long)N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++); + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++); cimg::fwrite(buf._data,N,nfile); to_write-=N; } - } else if (!cimg::type::is_float()) { // Save as P8 : Binary int32-valued 3d. + } else if (!cimg::type::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 buf(buf_size); - for (long to_write = _width*_height*_depth; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + CImg 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 (long i = (long)N; i>0; --i) *(ptrd++) = (int)*(ptr++); + 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. + } 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 buf(buf_size); - for (long to_write = _width*_height*_depth; to_write>0; ) { - const unsigned long N = cimg::min((unsigned long)to_write,buf_size); + CImg 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 (long i = (long)N; i>0; --i) *(ptrd++) = (float)*(ptr++); + for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++); cimg::fwrite(buf._data,N,nfile); to_write-=N; } @@ -37822,36 +52773,36 @@ namespace cimg_library { return *this; } - //! Save the image as a PFM file. + //! Save image as a PFM file. + /** + \param filename Filename, as a C-string. + **/ const CImg& save_pfm(const char *const filename) const { - return _save_pfm(0,filename); + get_mirror('y')._save_pfm(0,filename); + return *this; } - //! Save the image as a PFM file. + //! Save image as a PFM file \overloading. const CImg& save_pfm(std::FILE *const file) const { - return _save_pfm(file,0); + get_mirror('y')._save_pfm(file,0); + return *this; } - // Save the image as a PFM file (internal function). const CImg& _save_pfm(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_pfm() : Specified filename is (null).", + "save_pfm(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_pfm() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "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'.", + "save_pfm(): image instance is multispectral, only the three first channels will be saved " + "in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); @@ -37860,7 +52811,7 @@ namespace cimg_library { *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 = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3)); + 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); @@ -37868,10 +52819,10 @@ namespace cimg_library { switch (_spectrum) { case 1 : { // Scalar image CImg buf(buf_size); - for (int to_write = _width*_height; to_write>0; ) { - const unsigned int N = cimg::min((unsigned int)to_write,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 (int i = (int)N; i>0; --i) *(ptrd++) = (float)*(ptr_r++); + 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; @@ -37879,10 +52830,10 @@ namespace cimg_library { } break; case 2 : { // RG image CImg buf(buf_size); - for (int to_write = _width*_height; to_write>0; ) { - const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3); + 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 (int i = (int)N; i>0; --i) { + for (ulongT i = N; i>0; --i) { *(ptrd++) = (float)*(ptr_r++); *(ptrd++) = (float)*(ptr_g++); *(ptrd++) = 0; @@ -37894,10 +52845,10 @@ namespace cimg_library { } break; default : { // RGB image CImg buf(buf_size); - for (int to_write = _width*_height; to_write>0; ) { - const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3); + 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 (int i = (int)N; i>0; --i) { + for (ulongT i = N; i>0; --i) { *(ptrd++) = (float)*(ptr_r++); *(ptrd++) = (float)*(ptr_g++); *(ptrd++) = (float)*(ptr_b++); @@ -37912,25 +52863,33 @@ namespace cimg_library { return *this; } - // Save the image as a RGB file (internal). + //! Save image as a RGB file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_rgb(const char *const filename) const { + return _save_rgb(0,filename); + } + + //! Save image as a RGB file \overloading. + const CImg& save_rgb(std::FILE *const file) const { + return _save_rgb(file,0); + } + const CImg& _save_rgb(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_rgb() : Specified filename is (null).", + "save_rgb(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_rgb() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "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 unsigned int wh = _width*_height; + 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), @@ -37938,7 +52897,7 @@ namespace cimg_library { *ptr3 = _spectrum>2?data(0,0,0,2):0; switch (_spectrum) { case 1 : { // Scalar image - for (unsigned int k = 0; k& save_rgb(const char *const filename) const { - return _save_rgb(0,filename); + //! Save image as a RGBA file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_rgba(const char *const filename) const { + return _save_rgba(0,filename); } - //! Save the image as a RGB file. - const CImg& save_rgb(std::FILE *const file) const { - return _save_rgb(file,0); + //! Save image as a RGBA file \overloading. + const CImg& save_rgba(std::FILE *const file) const { + return _save_rgba(file,0); } - // Save the image as a RGBA file (internal). const CImg& _save_rgba(std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_rgba() : Specified filename is (null).", + "save_rgba(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_rgba() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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'.", + "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 unsigned int wh = _width*_height; + 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), @@ -38003,7 +52960,7 @@ namespace cimg_library { *ptr4 = _spectrum>3?data(0,0,0,3):0; switch (_spectrum) { case 1 : { // Scalar images - for (unsigned int k = 0; k& save_rgba(const char *const filename) const { - return _save_rgba(0,filename); + //! Save image as a TIFF file. + /** + \param filename Filename, as a C-string. + \param compression_type Type of data compression. Can be { 0=None | 1=LZW | 2=JPEG }. + \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 + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tif is not defined at compile time the + function uses CImg&save_other(const char*). + **/ + const CImg& 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 } - //! Save the image as a RGBA file. - const CImg& save_rgba(std::FILE *const file) const { - return _save_rgba(file,0); - } - - // Save a plane into a tiff file #ifdef cimg_use_tiff -#define _cimg_save_tiff(types,typed,compression) \ - if (!std::strcmp(types,pixel_type())) { const typed foo = (typed)0; return _save_tiff(tif,directory,foo,compression); } +#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 - const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t, const unsigned int compression) const { + const CImg& _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; @@ -38069,34 +53061,49 @@ namespace cimg_library { 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.0f/vx); + TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.0f/vy); + CImg 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::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); else if (cimg::type::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?(compression-1):COMPRESSION_NONE); + 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); + 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& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int compression) const { - _cimg_save_tiff("bool",unsigned char,compression); - _cimg_save_tiff("char",char,compression); - _cimg_save_tiff("unsigned char",unsigned char,compression); - _cimg_save_tiff("short",short,compression); - _cimg_save_tiff("unsigned short",unsigned short,compression); - _cimg_save_tiff("int",int,compression); - _cimg_save_tiff("unsigned int",unsigned int,compression); - _cimg_save_tiff("long",int,compression); - _cimg_save_tiff("unsigned long",unsigned int,compression); - _cimg_save_tiff("float",float,compression); - _cimg_save_tiff("double",float,compression); + const CImg& _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 CImgException(_cimg_instance - "save_tiff() : Unsupported pixel type '%s' for file '%s'.", - cimg_instance, - pixel_type(),filename?filename:"(FILE*)"); + throw CImgInstanceException(_cimg_instance + "save_tiff(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); return *this; } #endif - //! Save a file in TIFF format. + //! Save image as a MINC2 file. /** - - libtiff support is enabled by defining the precompilation - directive cimg_use_tif. - - - When libtiff is enabled, 2D and 3D (multipage) several - channel per pixel are supported for - char,uchar,short,ushort,float and double pixel type. - - - If cimg_use_tif is not defined at compilation time the - function uses CImg&save_other(const char*). - - \param compression 1:None, 2:CCITTRLE, 3:CCITTFAX3, 4:CCITTFAX4, 5:LZW, 6:JPEG - - \see CImg& save_other(const char*) - \see CImg& load_tiff(const char*) - **/ - const CImg& save_tiff(const char *const filename, const unsigned int compression=0) const { + \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& save_minc2(const char *const filename, + const char *const imitate_file=0) const { if (!filename) throw CImgArgumentException(_cimg_instance - "save_tiff() : Specified filename is (null).", - cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_tiff() : Empty instance, for file '%s'.", - cimg_instance, - filename); + "save_minc2(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } -#ifdef cimg_use_tiff - TIFF *tif = TIFFOpen(filename,"w"); - if (tif) { - cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z,compression); - TIFFClose(tif); - } else throw CImgException(_cimg_instance - "save_tiff() : Failed to open file '%s' for writing.", - cimg_instance, - filename); -#else - cimg::unused(compression); - return save_other(filename); -#endif - return *this; - } - - //! Save the image as a MINC2 file. - // (Original code by Haz-Edine Assemlal). - const CImg& save_minc2(const char *const filename) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_minc2() : Specified filename is (null).", - cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_minc2() : Empty instance, for file '%s'.", - cimg_instance, - filename); #ifndef cimg_use_minc2 - return save_other(filename); + cimg::unused(imitate_file); + return save_other(filename); #else - int nb_useful_dims = 0; - if (width()!=1) ++nb_useful_dims; - if (height()!=1) ++nb_useful_dims; - if (depth()!=1) ++nb_useful_dims; - if (spectrum()!=1) ++nb_useful_dims; - - mihandle_t hvol; - midimhandle_t *const hdims = (midimhandle_t*)std::malloc(nb_useful_dims*sizeof(midimhandle_t)); - CImg - start(nb_useful_dims,1,1,1,0), - count(nb_useful_dims,1,1,1,0); - CImg output(*this); - const char* dim_name[] = {"xspace", "yspace", "zspace", "time"}; - const midimclass_t dim_class[] = { MI_DIMCLASS_SPATIAL,MI_DIMCLASS_SPATIAL,MI_DIMCLASS_SPATIAL,MI_DIMCLASS_TIME }; - const midimattr_t dim_attr = MI_DIMATTR_REGULARLY_SAMPLED; - - // Strange stuffs here - /* The conversion from CImg data to Minc works only for - * specific orders (increasing dimensions and data in decreasing) - */ - CImg permutations, permutations_inv; - CImg - dim_vec = CImg::vector(width(),height(),depth(),spectrum()), - dim_sort = dim_vec.get_sort(permutations,true); - dim_vec.get_sort(permutations_inv,false); - - int d = 0; - cimg_foroff(dim_sort,dv) { - if (dim_sort(dv)<=1) continue; - micreate_dimension(dim_name[(int)permutations(dv)],dim_class[(int)permutations(dv)], - dim_attr,dim_sort(dv),&hdims[d]); - count(d++) = dim_sort(dv); - } - cimg_for(permutations_inv,ptr,char) if (*ptr<3) (*ptr)+='x'; else *ptr='c'; - output.permute_axes(permutations_inv.append(CImg::vector(0),'y').data()).mirror('y'); - - // End of strange stuffs. - micreate_volume(filename,nb_useful_dims,hdims,MI_TYPE_FLOAT,MI_CLASS_REAL,0,&hvol); - micreate_volume_image(hvol); - miset_volume_range(hvol,max(),min()); - miset_voxel_value_hyperslab(hvol,MI_TYPE_FLOAT,start,count,output.data()); - miclose_volume(hvol); - mifree_dimension_handle(*hdims); - return *this; + 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 (typeid(T)==typeid(unsigned char)) + wtr.setup_write_byte(); + else if (typeid(T)==typeid(int)) + wtr.setup_write_int(); + else if (typeid(T)==typeid(double)) + wtr.setup_write_double(); + else + wtr.setup_write_float(); + minc::save_standard_volume(wtr, this->_data); + return *this; #endif } - //! Save the image as an ANALYZE7.5 or NIFTI file. - const CImg& save_analyze(const char *const filename, const float *const voxsize=0) const { + //! 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& 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).", + "save_analyze(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_analyze() : Empty instance, for file '%s'.", - cimg_instance, - filename); + if (is_empty()) { cimg::fempty(0,filename); return *this; } std::FILE *file; - char header[348] = { 0 }, hname[1024] = { 0 }, iname[1024] = { 0 }; + CImg hname(1024), iname(1024); const char *const ext = cimg::split_filename(filename); - short datatype=-1; - std::memset(header,0,348); - if (!*ext) { cimg_snprintf(hname,sizeof(hname),"%s.hdr",filename); cimg_snprintf(iname,sizeof(iname),"%s.img",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,sizeof(iname)-1); std::sprintf(iname + std::strlen(iname)-3,"img"); + 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,sizeof(iname)-1); std::sprintf(hname + std::strlen(iname)-3,"hdr"); + 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,sizeof(hname)-1); *iname = 0; + std::strncpy(hname,filename,hname._width - 1); *iname = 0; } - int *const iheader = (int*)header; + + CImg header(*iname?348:352,1,1,1,0); + int *const iheader = (int*)header._data; *iheader = 348; - std::strcpy(header + 4,"CImg"); - std::strcpy(header + 14," "); - ((short*)(header + 36))[0] = 4096; - ((char*)(header + 38))[0] = 114; - ((short*)(header + 40))[0] = 4; - ((short*)(header + 40))[1] = _width; - ((short*)(header + 40))[2] = _height; - ((short*)(header + 40))[3] = _depth; - ((short*)(header + 40))[4] = _spectrum; + 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; @@ -38276,46 +53228,59 @@ namespace cimg_library { 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 long")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"long")) 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'.", + "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+112))[0] = 1; - ((float*)(header+76))[0] = 0; - if (voxsize) { - ((float*)(header+76))[1] = voxsize[0]; - ((float*)(header+76))[2] = voxsize[1]; - ((float*)(header+76))[3] = voxsize[2]; - } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1; + ((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,348,file); + 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 the image as a .cimg file. - const CImg& save_cimg(const char *const filename, const bool compression=false) const { - CImgList(*this,true).save_cimg(filename,compression); + //! 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& save_cimg(const char *const filename, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(filename,is_compressed); return *this; } - // Save the image as a .cimg file. - const CImg& save_cimg(std::FILE *const file, const bool compression=false) const { - CImgList(*this,true).save_cimg(file,compression); + //! Save image as a .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(file,is_compressed); return *this; } - //! Insert the image into an existing .cimg file, at specified coordinates. + //! 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& save_cimg(const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, @@ -38324,7 +53289,7 @@ namespace cimg_library { return *this; } - //! Insert the image into an existing .cimg file, at specified coordinates. + //! Save image as a sub-image into an existing .cimg file \overloading. const CImg& save_cimg(std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, @@ -38333,102 +53298,134 @@ namespace cimg_library { return *this; } - //! Save an empty .cimg file with specified dimensions. + //! 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::save_empty_cimg(filename,1,dx,dy,dz,dc); } - //! Save an empty .cimg file with specified dimensions. + //! 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::save_empty_cimg(file,1,dx,dy,dz,dc); } - // Save the image as an INRIMAGE-4 file (internal). - const CImg& _save_inr(std::FILE *const file, const char *const filename, const float *const voxsize) const { + //! 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& 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& save_inr(std::FILE *const file, const float *const voxel_size=0) const { + return _save_inr(file,0,voxel_size); + } + + const CImg& _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).", + "save_inr(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_inr() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + if (is_empty()) { cimg::fempty(file,filename); return *this; } - int inrpixsize=-1; + 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 (!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'", + "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"); - char header[257] = { 0 }; - int err = cimg_snprintf(header,sizeof(header),"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",_width,_height,_depth,_spectrum); - if (voxsize) err+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxsize[0],voxsize[1],voxsize[2]); - err+=std::sprintf(header + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); - std::memset(header + err,'\n',252 - err); - std::memcpy(header + 252,"##}\n",4); - cimg::fwrite(header,256,nfile); + CImg 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 the image as an INRIMAGE-4 file. - const CImg& save_inr(const char *const filename, const float *const voxsize=0) const { - return _save_inr(0,filename,voxsize); - } - - //! Save the image as an INRIMAGE-4 file. - const CImg& save_inr(std::FILE *const file, const float *const voxsize=0) const { - return _save_inr(file,0,voxsize); - } - - //! Save the image as a EXR file. + //! Save image as an OpenEXR file. + /** + \param filename Filename, as a C-string. + \note The OpenEXR file format is described here. + **/ const CImg& save_exr(const char *const filename) const { if (!filename) throw CImgArgumentException(_cimg_instance - "save_exr() : Specified filename is (null).", + "save_exr(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_exr() : Empty instance, for file '%s'.", - cimg_instance, - filename); + 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'.", + "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[_width*_height], *ptrd = ptrd0, rgba; + 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 + _width*_height; ptr_rPandore file specifications + for more information). + **/ + const CImg& 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& 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; } + 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& _save_pandore(std::FILE *const file, const char *const filename, const unsigned int colorspace) const { + const CImg& _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()]; \ @@ -38490,15 +53525,19 @@ namespace cimg_library { 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); \ + 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) { unsigned long ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ - else if (sizeof(unsigned int)==4) { unsigned int ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ - else if (sizeof(unsigned short)==4) { unsigned short ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ + if (sizeof(unsigned long)==4) { CImg 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 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 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'.",\ + "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) { \ @@ -38508,14 +53547,14 @@ namespace cimg_library { 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'.",\ + "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'.",\ + "save_pandore(): Unsupported datatype for file '%s'.",\ cimg_instance, \ filename?filename:"(FILE*)"); \ } \ @@ -38524,104 +53563,101 @@ namespace cimg_library { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_pandore() : Specified filename is (null).", + "save_pandore(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_pandore() : Empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + 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 }; + 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,"short",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 long",4); - _cimg_save_pandore_case(1,1,1,"long",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,"short",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 long",7); - _cimg_save_pandore_case(0,1,1,"long",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,"short",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 long",10); - _cimg_save_pandore_case(0,0,1,"long",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,"short",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 long",18); - _cimg_save_pandore_case(0,1,3,"long",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,"short",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 long",21); - _cimg_save_pandore_case(0,0,3,"long",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,"short",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 long",25); - _cimg_save_pandore_case(1,1,0,"long",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,"short",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 long",29); - _cimg_save_pandore_case(0,1,0,"long",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,"short",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 long",33); - _cimg_save_pandore_case(0,0,0,"long",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); @@ -38629,30 +53665,35 @@ namespace cimg_library { return *this; } - //! Save the image as a PANDORE-5 file. - const CImg& save_pandore(const char *const filename, const unsigned int colorspace=0) const { - return _save_pandore(0,filename,colorspace); + //! 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& save_raw(const char *const filename, const bool is_multiplexed=false) const { + return _save_raw(0,filename,is_multiplexed); } - //! Save the image as a PANDORE-5 file. - const CImg& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const { - return _save_pandore(file,0,colorspace); + //! 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& save_raw(std::FILE *const file, const bool is_multiplexed=false) const { + return _save_raw(file,0,is_multiplexed); } - // Save the image as a RAW file (internal). - const CImg& _save_raw(std::FILE *const file, const char *const filename, const bool multiplexed) const { + const CImg& _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).", + "save_raw(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_raw() : empty instance, for file '%s'.", - cimg_instance, - filename?filename:"(FILE*)"); + if (is_empty()) { cimg::fempty(file,filename); return *this; } std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - if (!multiplexed) cimg::fwrite(_data,size(),nfile); + if (!is_multiplexed) cimg::fwrite(_data,size(),nfile); else { CImg buf(_spectrum); cimg_forXYZ(*this,x,y,z) { @@ -38664,109 +53705,127 @@ namespace cimg_library { return *this; } - //! Save the image as a RAW file. - const CImg& save_raw(const char *const filename, const bool multiplexed=false) const { - return _save_raw(0,filename,multiplexed); - } - - //! Save the image as a RAW file. - const CImg& save_raw(std::FILE *const file, const bool multiplexed=false) const { - return _save_raw(file,0,multiplexed); - } - - //! Save the image as a video sequence file, using FFMPEG library. - const CImg& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int fps=25, const unsigned int bitrate=2048) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_ffmpeg() : Specified filename is (null).", - cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_ffmpeg() : Empty instance, for file '%s'.", - cimg_instance, - filename); - if (!fps) - throw CImgArgumentException(_cimg_instance - "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.", - cimg_instance, - filename); - -#ifndef cimg_use_ffmpeg - return save_ffmpeg_external(filename,first_frame,last_frame,"mpeg2video",fps,bitrate); -#else - get_split('z').save_ffmpeg(filename,first_frame,last_frame,fps,bitrate); -#endif + //! Save image as a .yuv video file. + /** + \param filename Filename, as a C-string. + \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& save_yuv(const char *const filename, const bool is_rgb=true) const { + get_split('z').save_yuv(filename,is_rgb); return *this; } - //! Save the image as a YUV video sequence file. - const CImg& save_yuv(const char *const filename, const bool rgb2yuv=true) const { - get_split('z').save_yuv(filename,rgb2yuv); + //! Save image as a .yuv video file \overloading. + /** + Same as save_yuv(const char*,bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_yuv(std::FILE *const file, const bool is_rgb=true) const { + get_split('z').save_yuv(file,is_rgb); return *this; } - //! Save the image as a YUV video sequence file. - const CImg& save_yuv(std::FILE *const file, const bool rgb2yuv=true) const { - get_split('z').save_yuv(file,rgb2yuv); - return *this; - } - - // Save OFF files (internal). + //! 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 described here. + **/ template - const CImg& _save_off(std::FILE *const file, const char *const filename, - const CImgList& primitives, const CImgList& colors) const { + const CImg& save_off(const CImgList& primitives, const CImgList& 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&,const CImgList&,const char*) const + with a file stream argument instead of a filename string. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file) const { + return _save_off(primitives,colors,file,0); + } + + template + const CImg& _save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file, const char *const filename) const { if (!file && !filename) throw CImgArgumentException(_cimg_instance - "save_off() : Specified filename is (null).", + "save_off(): Specified filename is (null).", cimg_instance); if (is_empty()) throw CImgInstanceException(_cimg_instance - "save_off() : Empty instance, for file '%s'.", + "save_off(): Empty instance, for file '%s'.", cimg_instance, filename?filename:"(FILE*)"); CImgList opacities; - char error_message[1024] = { 0 }; + CImg 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).", + "save_off(): Invalid specified 3d object, for file '%s' (%s).", cimg_instance, - filename?filename:"(FILE*)",error_message); + filename?filename:"(FILE*)",error_message.data()); const CImg 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))); + 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& color = l1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f; 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), + 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), + 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 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.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; - std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt); + const float + rt = color.atXY(xt,yt,0)/255.0f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; + 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.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; - std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2), + const float + rt = color.atXY(xt,yt,0)/255.0f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; + 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.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; - std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3), + const float + rt = color.atXY(xt,yt,0)/255.0f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f; + 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; } @@ -38775,191 +53834,252 @@ namespace cimg_library { return *this; } - //! Save OFF files. - template - const CImg& save_off(const char *const filename, - const CImgList& primitives, const CImgList& colors) const { - return _save_off(0,filename,primitives,colors); - } - - //! Save OFF files. - template - const CImg& save_off(std::FILE *const file, - const CImgList& primitives, const CImgList& colors) const { - return _save_off(file,0,primitives,colors); - } - - //! Save the image as a video sequence file, using the external tool 'ffmpeg'. - const CImg& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const char *const codec="mpeg2video", const unsigned int fps=25, const unsigned int bitrate=2048) const { - if (!filename) - throw CImgArgumentException(_cimg_instance - "save_ffmpeg_external() : Specified filename is (null).", - cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_ffmpeg_external() : Empty instance, for file '%s'.", - cimg_instance, - filename); - - get_split('z').save_ffmpeg_external(filename,first_frame,last_frame,codec,fps,bitrate); - return *this; - } - - //! Save the image using GraphicsMagick's gm. - /** Function that saves the image for other file formats that are not natively handled by CImg, - using the tool 'gm' from the GraphicsMagick package.\n - This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install - the GraphicsMagick package in order to get - this function working properly (see http://www.graphicsmagick.org ). + //! 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& 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()) - throw CImgInstanceException(_cimg_instance - "save_graphicsmagick_external() : Empty instance, for file '%s'.", - cimg_instance, - filename); - - char command[1024] = { 0 }, filetmp[512] = { 0 }; - std::FILE *file; - do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm"); - if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); - } while (file); - save_pnm(filetmp); - cimg_snprintf(command,sizeof(command),"%s -quality %u%% \"%s\" \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename); - cimg::system(command); - file = 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(filetmp); + const CImg& 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().save_video(filename,fps,codec,keep_open); return *this; } + CImgList list; + get_split('z').move_to(list); + list.save_video(filename,fps,codec,keep_open); return *this; } - //! Save an image as a gzipped file, using external tool 'gzip'. + //! 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 + FFmpeg. + It must be installed for the method to succeed. + **/ + const CImg& 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 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 + gzip. + It must be installed for the method to succeed. + **/ const CImg& save_gzip_external(const char *const filename) const { if (!filename) throw CImgArgumentException(_cimg_instance - "save_gzip_external() : Specified filename is (null).", + "save_gzip_external(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_gzip_external() : Empty instance, for file '%s'.", - cimg_instance, - filename); + if (is_empty()) { cimg::fempty(0,filename); return *this; } - char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + CImg 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(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - save(filetmp); - cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename); + save(filename_tmp); + cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); cimg::system(command); - file = std::fopen(filename,"rb"); + file = std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimg_instance - "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.", + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", cimg_instance, filename); else cimg::fclose(file); - std::remove(filetmp); + std::remove(filename_tmp); return *this; } - //! Save the image using ImageMagick's convert. - /** Function that saves the image for other file formats that are not natively handled by CImg, - using the tool 'convert' from the ImageMagick package.\n - This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install - the ImageMagick package in order to get - this function working properly (see http://www.imagemagick.org ). + //! 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 + GraphicsMagick. + It must be installed for the method to succeed. **/ - const CImg& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { + const CImg& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { if (!filename) throw CImgArgumentException(_cimg_instance - "save_imagemagick_external() : Specified filename is (null).", + "save_graphicsmagick_external(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_imagemagick_external() : Empty instance, for file '%s'.", - cimg_instance, - filename); + 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); - char command[1024] = { 0 }, filetmp[512] = { 0 }; +#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 command(1024), filename_tmp(256); std::FILE *file; do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm"); - if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + 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=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - save_pnm(filetmp); - cimg_snprintf(command,sizeof(command),"%s -quality %u%% \"%s\" \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename); +#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::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); cimg::system(command); - file = std::fopen(filename,"rb"); + file = std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimg_instance - "save_imagemagick_external() : Failed to save file '%s' with external command 'convert'.", + "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.", cimg_instance, filename); if (file) cimg::fclose(file); - std::remove(filetmp); + std::remove(filename_tmp); return *this; } - //! Save an image as a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net ) + //! 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 + ImageMagick. + It must be installed for the method to succeed. + **/ + const CImg& 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 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=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::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command); + file = 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 + (X)Medcon. + It must be installed for the method to succeed. + **/ const CImg& save_medcon_external(const char *const filename) const { if (!filename) throw CImgArgumentException(_cimg_instance - "save_medcon_external() : Specified filename is (null).", + "save_medcon_external(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_medcon_external() : Empty instance, for file '%s'.", - cimg_instance, - filename); + if (is_empty()) { cimg::fempty(0,filename); return *this; } - char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + CImg command(1024), filename_tmp(256), body(256); std::FILE *file; do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand()); - if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - save_analyze(filetmp); - cimg_snprintf(command,sizeof(command),"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp); + save_analyze(filename_tmp); + cimg_snprintf(command,command._width,"%s -w -c dicom -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); cimg::system(command); - std::remove(filetmp); - cimg::split_filename(filetmp,body); - cimg_snprintf(filetmp,sizeof(filetmp),"%s.img",body); - std::remove(filetmp); + 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 = std::fopen(filename,"rb"); + file = std_fopen(filename,"rb"); if (!file) { - cimg_snprintf(command,sizeof(command),"m000-%s",filename); - file = std::fopen(command,"rb"); + cimg_snprintf(command,command._width,"m000-%s",filename); + file = 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'.", + "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.", cimg_instance, filename); } @@ -38969,21 +54089,34 @@ namespace cimg_library { return *this; } - // Try to save the image if other extension is provided. + // 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 + ImageMagick or + GraphicsMagick. + 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& save_other(const char *const filename, const unsigned int quality=100) const { if (!filename) throw CImgArgumentException(_cimg_instance - "save_other() : Specified filename is (null).", + "save_other(): Specified filename is (null).", cimg_instance); - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "save_other() : Empty instance, for file '%s'.", - cimg_instance, - filename); + 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; + cimg::exception_mode(0); try { save_magick(filename); } catch (CImgException&) { try { save_imagemagick_external(filename,quality); } @@ -38994,27 +54127,33 @@ namespace cimg_library { } } } - cimg::exception_mode() = omode; + 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.", + "save_other(): Failed to save file '%s'. Format is not natively supported, " + "and no external commands succeeded.", cimg_instance, filename); return *this; } - // Get a 40x38 color logo of a 'danger' item (internal). - static CImg logo40x38() { - static bool first_time = true; - static CImg res(40,38,1,3); - if (first_time) { - 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 (unsigned int off = 0; off instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg get_serialize(const bool is_compressed=false) const { + return CImgList(*this,true).get_serialize(is_compressed); + } + + // [internal] Return a 40x38 color logo of a 'danger' item. + static CImg _logo40x38() { + CImg 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. + //! Represent a list of images CImg. template struct CImgList { - - //! Size of the list (number of images). - unsigned int _width; - - //! Allocation size of the list. - unsigned int _allocated_width; - - //! Pointer to the first list element. + unsigned int _width, _allocated_width; CImg *_data; - //! Define a CImgList::iterator. + //! Simple iterator type, to loop through each image of a list. + /** + \note + - The \c CImgList::iterator type is defined as a CImg*. + - You may use it like this: + \code + CImgList<> list; // Assuming this image list is not empty. + for (CImgList<>::iterator it = list.begin(); it* iterator; - //! Define a CImgList::const_iterator. + //! Simple const iterator type, to loop through each image of a \c const list instance. + /** + \note + - The \c CImgList::const_iterator type is defined to be a const CImg*. + - Similar to CImgList::iterator, but for constant list instances. + **/ typedef const CImg* const_iterator; - //! Value type. + //! Pixel value type. + /** + Refer to the pixels value type of the images in the list. + \note + - The \c CImgList::value_type type of a \c CImgList is defined to be a \c T. + It is then similar to CImg::value_type. + - \c CImgList::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 T-dependant types. + // Define common types related to template type T. typedef typename cimg::superset::type Tbool; typedef typename cimg::superset::type Tuchar; typedef typename cimg::superset::type Tchar; @@ -39064,8 +54221,8 @@ namespace cimg_library { typedef typename cimg::superset::type Tshort; typedef typename cimg::superset::type Tuint; typedef typename cimg::superset::type Tint; - typedef typename cimg::superset::type Tulong; - typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; typedef typename cimg::superset::type Tfloat; typedef typename cimg::superset::type Tdouble; typedef typename cimg::last::type boolT; @@ -39075,8 +54232,10 @@ namespace cimg_library { typedef typename cimg::last::type shortT; typedef typename cimg::last::type uintT; typedef typename cimg::last::type intT; - typedef typename cimg::last::type ulongT; - typedef typename cimg::last::type longT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; typedef typename cimg::last::type floatT; typedef typename cimg::last::type doubleT; @@ -39122,21 +54281,49 @@ namespace cimg_library { //-------------------------------------------------------- //! 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 an image list containing n empty images. + //! 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[_allocated_width = cimg::max(16UL,cimg::nearest_pow2(n))]; + if (n) _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; else { _allocated_width = 0; _data = 0; } } - //! Construct an image list containing n images with specified size. + //! 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) { @@ -39144,25 +54331,44 @@ namespace cimg_library { cimglist_apply(*this,assign)(width,height,depth,spectrum); } - //! Construct an image list containing n images with specified size, filled with specified value. + //! 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): + 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 an image list containing n images with specified size and specified pixel values (int version). + //! 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 width*height*depth*spectrum 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 unsigned int siz = width*height*depth*spectrum, nsiz = siz*n; \ + const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \ T *ptrd = _data->_data; \ va_list ap; \ va_start(ap,val1); \ - for (unsigned int l = 0, s = 0, i = 0; iwidth*height*depth*spectrum 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 a list containing n copies of the image img. + //! 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 - CImgList(const unsigned int n, const CImg& img, const bool shared=false): + CImgList(const unsigned int n, const CImg& img, const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(n); - cimglist_apply(*this,assign)(img,shared); + cimglist_apply(*this,assign)(img,is_shared); } - //! Construct an image list from one image. + //! 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 - explicit CImgList(const CImg& img, const bool shared=false): + explicit CImgList(const CImg& img, const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(1); - _data[0].assign(img,shared); + _data[0].assign(img,is_shared); } - //! Construct an image list from two images. + //! 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 - CImgList(const CImg& img1, const CImg& img2, const bool shared=false): + CImgList(const CImg& img1, const CImg& img2, const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(2); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); } - //! Construct an image list from three images. + //! 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 - CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false): + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(3); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); } - //! Construct an image list from four images. + //! 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 - CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared=false): + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(4); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); + _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 an image list from five images. + //! 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 CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const CImg& img5, const bool shared=false): + const CImg& img5, const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(5); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); + _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 an image list from six images. + //! 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 CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const CImg& img5, const CImg& img6, const bool shared=false): + const CImg& img5, const CImg& img6, const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(6); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); _data[5].assign(img6,shared); + _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 an image list from seven images. + //! 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 CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const CImg& img5, const CImg& img6, const CImg& img7, const bool shared=false): + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(7); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); + _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 an image list from eight images. + //! 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 CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool shared=false): + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false): _width(0),_allocated_width(0),_data(0) { assign(8); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); _data[7].assign(img8,shared); + _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); } - //! Default copy constructor. + //! 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 CImgList(const CImgList& 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& list):_width(0),_allocated_width(0),_data(0) { assign(list._width); cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared); } - //! Advanced copy constructor. + //! 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 - CImgList(const CImgList& list, const bool shared):_width(0),_allocated_width(0),_data(0) { + CImgList(const CImgList& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) { assign(list._width); - cimglist_for(*this,l) _data[l].assign(list[l],shared); + cimglist_for(*this,l) _data[l].assign(list[l],is_shared); } - //! Construct an image list from a filename. + //! 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 an image list from a display. - explicit CImgList(const CImgDisplay &disp):_width(0),_allocated_width(0),_data(0) { + //! 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 shared instance of the list. + //! Return a list with elements being shared copies of images in the list instance. + /** + \note list2 = list1.get_shared() is equivalent to list2.assign(list1,true). + **/ CImgList get_shared() { CImgList res(_width); cimglist_for(*this,l) res[l].assign(_data[l],true); return res; } - //! Return a shared instance of the list. + //! Return a list with elements being shared copies of images in the list instance \const. const CImgList get_shared() const { CImgList res(_width); cimglist_for(*this,l) res[l].assign(_data[l],true); return res; } - //! In-place version of the default constructor and default destructor. + //! Destructor \inplace. + /** + \see CImgList(). + **/ CImgList& assign() { delete[] _data; _width = _allocated_width = 0; @@ -39309,19 +54618,33 @@ namespace cimg_library { return *this; } - //! In-place version of the corresponding constructor. + //! Destructor \inplace. + /** + Equivalent to assign(). + \note Only here for compatibility with STL naming conventions. + **/ + CImgList& clear() { + return assign(); + } + + //! Construct list containing empty images \inplace. + /** + \see CImgList(unsigned int). + **/ CImgList& assign(const unsigned int n) { - if (n) { - if (_allocated_width(n<<2)) { - delete[] _data; - _data = new CImg[_allocated_width=cimg::max(16UL,cimg::nearest_pow2(n))]; - } - _width = n; - } else assign(); + if (!n) return assign(); + if (_allocated_width(n<<2)) { + delete[] _data; + _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; + } + _width = n; return *this; } - //! In-place version of the corresponding constructor. + //! Construct list containing images of specified size \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int). + **/ CImgList& 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); @@ -39329,147 +54652,193 @@ namespace cimg_library { return *this; } - //! In-place version of the corresponding constructor. + //! 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& assign(const unsigned int n, const unsigned int width, const unsigned int height, - const unsigned int depth, const unsigned int spectrum, const T val) { + const unsigned int depth, const unsigned int spectrum, const T& val) { assign(n); cimglist_apply(*this,assign)(width,height,depth,spectrum,val); return *this; } - //! In-place version of the corresponding constructor. + //! 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& 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; } - //! In-place version of the corresponding constructor. + //! 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& 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, ...) { + const unsigned int depth, const unsigned int spectrum, + const double val0, const double val1, ...) { _CImgList_stdarg(double); return *this; } - //! In-place version of the copy constructor. - CImgList& assign(const CImgList& list, const bool shared=false) { - if (this==&list) return *this; - CImgList res(list._width); - cimglist_for(res,l) res[l].assign(list[l],shared); - return res.move_to(*this); - } - + //! Construct list containing copies of an input image \inplace. + /** + \see CImgList(unsigned int, const CImg&, bool). + **/ template - CImgList& assign(const CImgList& list, const bool shared=false) { - assign(list._width); - cimglist_for(*this,l) _data[l].assign(list[l],shared); - return *this; - } - - //! In-place version of the corresponding constructor. - template - CImgList& assign(const unsigned int n, const CImg& img, const bool shared=false) { + CImgList& assign(const unsigned int n, const CImg& img, const bool is_shared=false) { assign(n); - cimglist_apply(*this,assign)(img,shared); + cimglist_apply(*this,assign)(img,is_shared); return *this; } - //! In-place version of the corresponding constructor. + //! Construct list from one image \inplace. + /** + \see CImgList(const CImg&, bool). + **/ template - CImgList& assign(const CImg& img, const bool shared=false) { + CImgList& assign(const CImg& img, const bool is_shared=false) { assign(1); - _data[0].assign(img,shared); + _data[0].assign(img,is_shared); return *this; } - //! In-place version of the corresponding constructor. + //! Construct list from two images \inplace. + /** + \see CImgList(const CImg&, const CImg&, bool). + **/ template - CImgList& assign(const CImg& img1, const CImg& img2, const bool shared=false) { + CImgList& assign(const CImg& img1, const CImg& img2, const bool is_shared=false) { assign(2); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); return *this; } - //! In-place version of the corresponding constructor. + //! Construct list from three images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, bool). + **/ template - CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false) { + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false) { assign(3); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); return *this; } - //! In-place version of the corresponding constructor. + //! Construct list from four images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const bool shared=false) { + const bool is_shared=false) { assign(4); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); + _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; } - //! In-place version of the corresponding constructor. + //! Construct list from five images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const CImg& img5, const bool shared=false) { + const CImg& img5, const bool is_shared=false) { assign(5); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); + _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; } - //! In-place version of the corresponding constructor. + //! Construct list from six images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, bool). + **/ template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const CImg& img5, const CImg& img6, const bool shared=false) { + const CImg& img5, const CImg& img6, const bool is_shared=false) { assign(6); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); _data[5].assign(img6,shared); + _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; } - //! In-place version of the corresponding constructor. + //! Construct list from seven images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, bool). + **/ template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, - const CImg& img5, const CImg& img6, const CImg& img7, const bool shared=false) { + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false) { assign(7); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); + _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; } - //! In-place version of the corresponding constructor. + //! Construct list from eight images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, const CImg&, bool). + **/ template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, - const bool shared=false) { + const bool is_shared=false) { assign(8); - _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared); - _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); _data[7].assign(img8,shared); + _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; } - //! In-place version of the corresponding constructor. + //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace. + /** + \see CImgList(const CImgList&, bool is_shared). + **/ + template + CImgList& assign(const CImgList& 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& assign(const CImgList& list, const bool is_shared=false) { + if (this==&list) return *this; + CImgList 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& assign(const char *const filename) { return load(filename); } - //! In-place version of the corresponding constructor. + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ CImgList& assign(const CImgDisplay &disp) { return assign(CImg(disp)); } - //! In-place version of the default constructor. + //! Transfer the content of the list instance to another list. /** - Equivalent to assign(). - \note - - It has been defined for compatibility with STL naming conventions. - \sa assign(). + \param list Destination list. + \note When returning, the current list instance is empty and the initial content of \c list is destroyed. **/ - CImgList& clear() { - return assign(); - } - - //! Move the content of the image instance list into another one. template CImgList& move_to(CImgList& list) { list.assign(_width); @@ -39481,6 +54850,13 @@ namespace cimg_library { 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 CImgList& move_to(CImgList& list, const unsigned int pos) { if (is_empty()) return list; @@ -39488,26 +54864,41 @@ namespace cimg_library { 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]); + 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 of two CImgList instances (use with care !) + //! 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& swap(CImgList& list) { - cimg::swap(_width,list._width); - cimg::swap(_allocated_width,list._allocated_width); + 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 as an argument. + \code + void f(const CImgList& list=CImgList::empty()); + \endcode + **/ static CImgList& empty() { static CImgList _empty; return _empty.assign(); } + //! Return a reference to an empty list \const. + static const CImgList& const_empty() { + static const CImgList _empty; + return _empty; + } + //@} //------------------------------------------ // @@ -39515,12 +54906,15 @@ namespace cimg_library { //@{ //------------------------------------------ - //! Return a reference to the i-th element of the image list. + //! Return a reference to one image element of the list. + /** + \param pos Indice of the image element. + **/ CImg& operator()(const unsigned int pos) { #if cimg_verbosity>=3 if (pos>=_width) { cimg::warn(_cimglist_instance - "operator() : Invalid image request, at position [%u].", + "operator(): Invalid image request, at position [%u].", cimglist_instance, pos); return *_data; @@ -39529,82 +54923,145 @@ namespace cimg_library { return _data[pos]; } + //! Return a reference to one image of the list. + /** + \param pos Indice of the image element. + **/ const CImg& operator()(const unsigned int pos) const { return const_cast*>(this)->operator()(pos); } - //! Return a reference to (x,y,z,c) pixel of the pos-th image of the list + //! 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 list(n,x,y,z,c) is equivalent to list[n](x,y,z,c). + **/ 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 address of the image vector. - operator const CImg*() const { - return _data; - } - + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ operator CImg*() { return _data; } - //! Operator=(). + //! Return pointer to the first image of the list \const. + operator const CImg*() const { + return _data; + } + + //! Construct list from one image \inplace. + /** + \param img Input image to copy in the constructed list. + \note list = img; is equivalent to list.assign(img);. + **/ template CImgList& operator=(const CImg& img) { return assign(img); } - //! Operator=(). - CImgList& operator=(const CImgDisplay& disp) { - return assign(disp); - } - - //! Operator=(). + //! Construct list from another list. + /** + \param list Input list to copy. + \note list1 = list2 is equivalent to list1.assign(list2);. + **/ template CImgList& operator=(const CImgList& list) { return assign(list); } + //! Construct list from another list \specialization. CImgList& operator=(const CImgList& list) { return assign(list); } - //! Operator=(). + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ CImgList& operator=(const char *const filename) { return assign(filename); } - //! Operator+() (unary). + //! Construct list from the content of a display window \inplace. /** - Writing '+list' is a convenient shortcut to 'CImgList(list,false)' - (forces a copy with non-shared elements). - **/ + \see CImgList(const CImgDisplay&). + **/ + CImgList& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return a non-shared copy of a list. + /** + \note +list is equivalent to CImgList(list,false). + It forces the copy to have non-shared elements. + **/ CImgList operator+() const { return CImgList(*this,false); } - //! Operator,(). + //! 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 CImgList& operator,(const CImg& img) { return insert(img); } - //! Operator,(). + //! Return a copy of the list instance, where image \c img has been inserted at the end \const. + template + CImgList operator,(const CImg& 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 CImgList& operator,(const CImgList& list) { return insert(list); } - //! Operator>(). + //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const. + template + CImgList& operator,(const CImgList& 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 { 'x' | 'y' | 'z' | 'c' }. + \note list>'x' is equivalent to list.get_append('x'). + **/ CImg operator>(const char axis) const { return get_append(axis,0); } - //! Operator<(). + //! Return list corresponding to the splitting of all images of the instance list along specified axis. + /** + \param axis Axis used for image splitting. + \note list<'x' is equivalent to list.get_split('x'). + **/ CImgList operator<(const char axis) const { return get_split(axis); } @@ -39616,41 +55073,60 @@ namespace cimg_library { //@{ //------------------------------------- - //! Return a string describing the type of the image pixels in the list (template parameter \p T). + //! 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 "unknown" is returned. + **/ static const char* pixel_type() { return cimg::type::string(); } - //! Return the size of the list. + //! 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. + //! 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 a pointer to the image buffer. + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ CImg *data() { return _data; } + //! Return pointer to the first image of the list \const. const CImg *data() const { return _data; } - //! Return a pointer to the image buffer. + //! Return pointer to the pos-th image of the list. + /** + \param pos Indice of the image element to access. + \note list.data(n); is equivalent to list.data + n;. + **/ #if cimg_verbosity>=3 - CImg *data(const unsigned int l) { - if (l>=size()) { + CImg *data(const unsigned int pos) { + if (pos>=size()) cimg::warn(_cimglist_instance - "data() : Invalid pointer request, at position [%u].", + "data(): Invalid pointer request, at position [%u].", cimglist_instance, - l); - return _data; - } - return _data + l; + pos); + return _data + pos; } const CImg *data(const unsigned int l) const { @@ -39661,245 +55137,361 @@ namespace cimg_library { return _data + l; } + //! Return pointer to the pos-th image of the list \const. const CImg *data(const unsigned int l) const { return _data + l; } #endif - //! Returns an iterator to the beginning of the vector (STL-compliant name). + //! 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; } - //! Returns an iterator just past the last element (STL-compliant name). + //! 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; } - //! Returns a reference to the first element (STL-compliant name). + //! Return reference to the first image of the list. + /** + **/ CImg& front() { return *_data; } + //! Return reference to the first image of the list \const. const CImg& front() const { return *_data; } - //! Return a reference to the last image (STL-compliant name). + //! Return a reference to the last image of the list. + /** + **/ const CImg& back() const { return *(_data + _width - 1); } + //! Return a reference to the last image of the list \const. CImg& back() { return *(_data + _width - 1); } - //! Read an image in specified position. + //! Return pos-th image of the list. + /** + \param pos Indice of the image element to access. + **/ CImg& at(const int pos) { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "at() : Empty instance.", + "at(): Empty instance.", cimglist_instance); - return _data[pos<0?0:pos>=(int)_width?(int)_width-1:pos]; + return _data[cimg::cut(pos,0,width() - 1)]; } - //! Get pixel value with Dirichlet boundary conditions. - T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + //! 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 list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + 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); } - T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + //! 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); } - //! Get pixel value with Neumann boundary conditions. + //! 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 list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ 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.", + "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.", + "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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c); + return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); } - //! Get pixel value with Dirichlet boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z). - T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + 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); } - T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + //! 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); } - //! Get pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z). - T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + 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.", + "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.", + "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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c); + 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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c); + return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); } - //! Get pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y). - T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + 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); } - T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + //! 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); } - //! Get pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y). + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ 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.", + "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.", + "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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c); + 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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c); + return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); } - //! Get pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x). - T& atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + 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); } - T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + //! 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); } - //! Get pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x). + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ 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.", + "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.", + "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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c); + 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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c); + return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); } - //! Get pixel value with Dirichlet boundary conditions for the first coordinates (\c pos). - T& atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) { + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + 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); } - T atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) const { + //! 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); } - //! Get pixel value with Neumann boundary conditions for the first coordinates (\c pos). + //! 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 list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(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) { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "atN() : Empty 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.", + "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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c); + 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[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c); + return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); } //! Return a C-string containing the values of all images in the instance list. + /** + \param separator Character separator set between consecutive pixel values. + \param max_size Maximum size of the returned string. + \note The result is returne as a CImg image whose pixel buffer contains the desired C-string. + **/ CImg value_string(const char separator=',', const unsigned int max_size=0) const { if (is_empty()) return CImg(1,1,1,1,0); CImgList items; - for (unsigned int l = 0; l<_width-1; ++l) { + for (unsigned int l = 0; l<_width - 1; ++l) { CImg item = _data[l].value_string(separator,0); item.back() = separator; item.move_to(items); } - _data[_width-1].value_string(separator,0).move_to(items); + _data[_width - 1].value_string(separator,0).move_to(items); CImg res; (items>'x').move_to(res); if (max_size) { res.crop(0,max_size); res(max_size) = 0; } return res; @@ -39912,27 +55504,36 @@ namespace cimg_library { //@{ //------------------------------------- - //! Return \p true if list is empty. + //! Return \c true if list is empty. + /** + **/ bool is_empty() const { return (!_data || !_width); } - //! Return \p true if list if of specified size. - bool is_sameN(const unsigned int n) const { - return (_width==n); + //! 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; } - //! Return \p true if list if of specified size. + //! Test if number of image elements is equal between two images lists. + /** + \param list Input list to compare with. + **/ template bool is_sameN(const CImgList& list) const { - return (_width==list._width); + return is_sameN(list._width); } - // Define useful dimension check functions. - // (not documented because they are macro-generated). + // 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 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); \ @@ -39940,17 +55541,22 @@ namespace cimg_library { #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 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_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 { \ + 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); \ } \ @@ -39959,7 +55565,7 @@ namespace cimg_library { bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \ } \ template bool is_same##axis(const CImgList& list) const { \ - const unsigned int lmin = cimg::min(_width,list._width); \ + const unsigned int lmin = std::min(_width,list._width); \ bool res = true; for (unsigned int l = 0; l bool is_sameN##axis(const unsigned int n, const CImg& img) const { \ @@ -39993,30 +55599,67 @@ namespace cimg_library { _cimglist_def_is_same3(X,Z,C) _cimglist_def_is_same3(Y,Z,C) - bool is_sameXYZC(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const { + //! 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; } - bool is_sameNXYZC(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const { + //! 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); } - //! Return \c true if the list contains the pixel (n,x,y,z,c). + //! 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(); } - //! Return \c true if the list contains the image (n). + //! 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; } - //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z,c). + //! 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 bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const { if (is_empty()) return false; @@ -40024,50 +55667,87 @@ namespace cimg_library { return false; } - //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z). + //! 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 bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { t c; return contains(pixel,n,x,y,z,c); } - //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y). + //! 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 bool contains(const T& pixel, t& n, t& x, t&y) const { t z, c; return contains(pixel,n,x,y,z,c); } - //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x). + //! 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 bool contains(const T& pixel, t& n, t& x) const { t y, z, c; return contains(pixel,n,x,y,z,c); } - //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n). + //! 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 bool contains(const T& pixel, t& n) const { t x, y, z, c; return contains(pixel,n,x,y,z,c); } - //! Return \c true if one of the image list contains the specified referenced value. + //! 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); } - //! Return \c true if the list contains the image 'img'. If true, returns the position (n) of the image in the list. + //! 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 bool contains(const CImg& img, t& n) const { if (is_empty()) return false; const CImg *const ptr = &img; - cimglist_for(*this,i) if (_data+i==ptr) { n = (t)i; return true; } + cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; } return false; } - //! Return \c true if the list contains the image img. + //! Test if the list contains the image img. + /** + \param img Reference to image to test. + **/ bool contains(const CImg& img) const { unsigned int n; return contains(img,n); @@ -40081,10 +55761,12 @@ namespace cimg_library { //------------------------------------- //! Return a reference to the minimum pixel value of the instance list. + /** + **/ T& min() { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "min() : Empty instance.", + "min(): Empty instance.", cimglist_instance); T *ptr_min = _data->_data; T min_value = *ptr_min; @@ -40095,10 +55777,11 @@ namespace cimg_library { return *ptr_min; } + //! Return a reference to the minimum pixel value of the instance list \const. const T& min() const { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "min() : Empty instance.", + "min(): Empty instance.", cimglist_instance); const T *ptr_min = _data->_data; T min_value = *ptr_min; @@ -40110,10 +55793,12 @@ namespace cimg_library { } //! Return a reference to the maximum pixel value of the instance list. + /** + **/ T& max() { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "max() : Empty instance.", + "max(): Empty instance.", cimglist_instance); T *ptr_max = _data->_data; T max_value = *ptr_max; @@ -40124,10 +55809,11 @@ namespace cimg_library { return *ptr_max; } + //! Return a reference to the maximum pixel value of the instance list \const. const T& max() const { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "max() : Empty instance.", + "max(): Empty instance.", cimglist_instance); const T *ptr_max = _data->_data; T max_value = *ptr_max; @@ -40138,12 +55824,15 @@ namespace cimg_library { return *ptr_max; } - //! Return a reference to the minimum pixel value of the instance list. + //! 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 T& min_max(t& max_val) { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "min_max() : Empty instance.", + "min_max(): Empty instance.", cimglist_instance); T *ptr_min = _data->_data; T min_value = *ptr_min, max_value = min_value; @@ -40159,11 +55848,15 @@ namespace cimg_library { 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 const T& min_max(t& max_val) const { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "min_max() : Empty instance.", + "min_max(): Empty instance.", cimglist_instance); const T *ptr_min = _data->_data; T min_value = *ptr_min, max_value = min_value; @@ -40179,12 +55872,15 @@ namespace cimg_library { return *ptr_min; } - //! Return a reference to the minimum pixel value of the instance list. + //! 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 T& max_min(t& min_val) { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "max_min() : Empty instance.", + "max_min(): Empty instance.", cimglist_instance); T *ptr_max = _data->_data; T min_value = *ptr_max, max_value = min_value; @@ -40200,11 +55896,12 @@ namespace cimg_library { return *ptr_max; } + //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const. template const T& max_min(t& min_val) const { if (is_empty()) throw CImgInstanceException(_cimglist_instance - "max_min() : Empty instance.", + "max_min(): Empty instance.", cimglist_instance); const T *ptr_max = _data->_data; T min_value = *ptr_max, max_value = min_value; @@ -40227,76 +55924,102 @@ namespace cimg_library { //@{ //--------------------------- - //! Insert a copy of the image \p img into the current image list, at position \p pos. + //! 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 - CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool shared=false) { + CImgList& insert(const CImg& 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.", + "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 (shared) + 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).", + "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 *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0; - if (!_width || !_data) { + CImg *const new_data = (++_width>_allocated_width)?new CImg[_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) { + if (new_data) { // Insert with re-allocation. if (npos) std::memcpy(new_data,_data,sizeof(CImg)*npos); - if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); - std::memset(_data,0,sizeof(CImg)*(_width-1)); + if (npos!=_width - 1) std::memcpy(new_data + npos + 1,_data + npos,sizeof(CImg)*(_width - 1 - npos)); + std::memset(_data,0,sizeof(CImg)*(_width - 1)); delete[] _data; _data = new_data; - } else if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); - _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0; + } else if (npos!=_width - 1) // Insert without re-allocation. + std::memmove(_data + npos + 1,_data + npos,sizeof(CImg)*(_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; } - CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool shared=false) { + //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization. + CImgList& insert(const CImg& 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.", + "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 *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0; - if (!_width || !_data) { + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1): + (_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list. _data = new_data; - if (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; + 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) { + if (new_data) { // Insert with re-allocation. if (npos) std::memcpy(new_data,_data,sizeof(CImg)*npos); - if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); - if (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; + if (npos!=_width - 1) std::memcpy(new_data + npos + 1,_data + npos,sizeof(CImg)*(_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]._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(_data,0,sizeof(CImg)*(_width-1)); + std::memset(_data,0,sizeof(CImg)*(_width - 1)); delete[] _data; _data = new_data; - } else { - if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg)*(_width-1-npos)); - if (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 { // Insert without re-allocation. + if (npos!=_width - 1) std::memmove(_data + npos + 1,_data + npos,sizeof(CImg)*(_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]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; + _data[npos]._data = 0; _data[npos] = img; } } @@ -40304,12 +56027,17 @@ namespace cimg_library { return *this; } + //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance. template - CImgList get_insert(const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { - return (+*this).insert(img,pos,shared); + CImgList get_insert(const CImg& 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& insert(const unsigned int n, const unsigned int pos=~0U) { CImg empty; if (!n) return *this; @@ -40318,85 +56046,114 @@ namespace cimg_library { return *this; } + //! Insert n empty images img into the current image list, at position \p pos \newinstance. CImgList get_insert(const unsigned int n, const unsigned int pos=~0U) const { return (+*this).insert(n,pos); } - //! Insert n copies of the image \p img into the current image list, at position \p 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 - CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) { + CImgList& insert(const unsigned int n, const CImg& 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,shared); - for (unsigned int i = 1; i - CImgList get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { - return (+*this).insert(n,img,pos,shared); + CImgList get_insert(const unsigned int n, const CImg& 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 \p list into the current image list, starting from position \p pos. + //! 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 - CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const bool shared=false) { + CImgList& insert(const CImgList& 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,shared); - else insert(CImgList(list),npos,shared); + if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared); + else insert(CImgList(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 - CImgList get_insert(const CImgList& list, const unsigned int pos=~0U, const bool shared=false) const { - return (+*this).insert(list,pos,shared); + CImgList get_insert(const CImgList& 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 \p list at position \p pos of the current list. + //! 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 - CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool shared=false) { + CImgList& insert(const unsigned int n, const CImgList& 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 - CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool shared=false) const { - return (+*this).insert(n,list,pos,shared); + CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, + const bool is_shared=false) const { + return (+*this).insert(n,list,pos,is_shared); } - //! Remove the images from positions \p pos1 to \p pos2. + //! Remove all images between from indexes. + /** + \param pos1 Starting index of the removal. + \param pos2 Ending index of the removal. + **/ CImgList& remove(const unsigned int pos1, const unsigned int pos2) { const unsigned int npos1 = pos1=_width) throw CImgArgumentException(_cimglist_instance - "remove() : Invalid remove request at positions %u->%u.", + "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.", + "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<=8) { // Removing items without reallocation. - if (npos1!=_width) std::memmove(_data+npos1,_data+npos2+1,sizeof(CImg)*(_width - npos1)); + if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation. + if (npos1!=_width) std::memmove(_data + npos1,_data + npos2 + 1,sizeof(CImg)*(_width - npos1)); std::memset(_data + _width,0,sizeof(CImg)*nb); } else { // Removing items with reallocation. _allocated_width>>=2; - while (_allocated_width>8 && _width<(_allocated_width>>1)) _allocated_width>>=1; + while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1; CImg *const new_data = new CImg[_allocated_width]; if (npos1) std::memcpy(new_data,_data,sizeof(CImg)*npos1); - if (npos1!=_width) std::memcpy(new_data+npos1,_data+npos2+1,sizeof(CImg)*(_width-npos1)); - if (_width!=_allocated_width) std::memset(new_data+_width,0,sizeof(_allocated_width - _width)); - std::memset(_data,0,sizeof(CImg)*(_width+nb)); + if (npos1!=_width) std::memcpy(new_data + npos1,_data + npos2 + 1,sizeof(CImg)*(_width - npos1)); + if (_width!=_allocated_width) std::memset(new_data + _width,0,sizeof(CImg)*(_allocated_width - _width)); + std::memset(_data,0,sizeof(CImg)*(_width + nb)); delete[] _data; _data = new_data; } @@ -40404,147 +56161,189 @@ namespace cimg_library { return *this; } + //! Remove all images between from indexes \newinstance. CImgList get_remove(const unsigned int pos1, const unsigned int pos2) const { return (+*this).remove(pos1,pos2); } - //! Remove the image at position \p pos from the image list. + //! Remove image at index \c pos from the image list. + /** + \param pos Index of the image to remove. + **/ CImgList& remove(const unsigned int pos) { return remove(pos,pos); } + //! Remove image at index \c pos from the image list \newinstance. CImgList get_remove(const unsigned int pos) const { return (+*this).remove(pos); } - //! Remove the last image from the image list. + //! Remove last image. + /** + **/ CImgList& remove() { - return remove(_width-1); + return remove(_width - 1); } + //! Remove last image \newinstance. CImgList get_remove() const { return (+*this).remove(); } //! Reverse list order. CImgList& reverse() { - for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width-1-l]); + for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]); return *this; } + //! Reverse list order \newinstance. CImgList get_reverse() const { return (+*this).reverse(); } - //! Get a sub-list. - CImgList& images(const unsigned int i0, const unsigned int i1) { - return get_images(i0,i1).move_to(*this); - } - - CImgList get_images(const unsigned int i0, const unsigned int i1) const { - if (i0>i1 || i1>=_width) - throw CImgArgumentException(_cimglist_instance - "images() : Specified sub-list indices (%u->%u) are out of bounds.", - cimglist_instance, - i0,i1); - CImgList res(i1-i0+1); - cimglist_for(res,l) res[l].assign(_data[i0+l]); - return res; - } - - //! Get a shared sub-list. - CImgList get_shared_images(const unsigned int i0, const unsigned int i1) { - if (i0>i1 || i1>=_width) - throw CImgArgumentException(_cimglist_instance - "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.", - cimglist_instance, - i0,i1); - CImgList res(i1-i0+1); - cimglist_for(res,l) res[l].assign(_data[i0+l],true); - return res; - } - - const CImgList get_shared_images(const unsigned int i0, const unsigned int i1) const { - if (i0>i1 || i1>=_width) - throw CImgArgumentException(_cimglist_instance - "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.", - cimglist_instance, - i0,i1); - CImgList res(i1-i0+1); - cimglist_for(res,l) res[l].assign(_data[i0+l],true); - return res; - } - - //! Return a single image which is the concatenation of all images of the current CImgList instance. + //! Return a sublist. /** - \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'. - \param align : specify the alignment for image concatenation. Can be '0' (top), '0.5' (center) or '1' (bottom) for instance. - \return A CImg image corresponding to the concatenation is returned. + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList& images(const unsigned int pos0, const unsigned int pos1) { + return get_images(pos0,pos1).move_to(*this); + } + + //! Return a sublist \newinstance. + CImgList 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 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 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 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 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 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 { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. **/ CImg get_append(const char axis, const float align=0) const { if (is_empty()) return CImg(); if (_width==1) return +((*this)[0]); unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0; CImg res; - switch (cimg::uncase(axis)) { + switch (cimg::lowercase(axis)) { case 'x' : { // Along the X-axis. cimglist_for(*this,l) { const CImg& img = (*this)[l]; - dx+=img._width; dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); + 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,0); + res.assign(dx,dy,dz,dc,(T)0); if (res) cimglist_for(*this,l) { - res.draw_image(pos, - (int)(align*(dy-(*this)[l]._height)), - (int)(align*(dz-(*this)[l]._depth)), - (int)(align*(dc-(*this)[l]._spectrum)), - (*this)[l]); - pos+=(*this)[l]._width; + const CImg& 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& img = (*this)[l]; - dx = cimg::max(dx,img._width); dy+=img._height; dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); + 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,0); + res.assign(dx,dy,dz,dc,(T)0); if (res) cimglist_for(*this,l) { - res.draw_image((int)(align*(dx-(*this)[l]._width)), - pos, - (int)(align*(dz-(*this)[l]._depth)), - (int)(align*(dc-(*this)[l]._spectrum)), - (*this)[l]); - pos+=(*this)[l]._height; + const CImg& 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& img = (*this)[l]; - dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz+=img._depth; dc = cimg::max(dc,img._spectrum); + 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,0); + res.assign(dx,dy,dz,dc,(T)0); if (res) cimglist_for(*this,l) { - res.draw_image((int)(align*(dx-(*this)[l]._width)), - (int)(align*(dy-(*this)[l]._height)), - pos, - (int)(align*(dc-(*this)[l]._spectrum)), - (*this)[l]); - pos+=(*this)[l]._depth; + const CImg& 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& img = (*this)[l]; - dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc+=img._spectrum; + 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,0); + res.assign(dx,dy,dz,dc,(T)0); if (res) cimglist_for(*this,l) { - res.draw_image((int)(align*(dx-(*this)[l]._width)), - (int)(align*(dy-(*this)[l]._height)), - (int)(align*(dz-(*this)[l]._depth)), - pos, - (*this)[l]); - pos+=(*this)[l]._spectrum; + const CImg& 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; } } } @@ -40552,53 +56351,77 @@ namespace cimg_library { } //! Return a list where each image has been split along the specified axis. - CImgList& split(const char axis, const int nb=0) { + /** + \param axis Axis to split images along. + \param nb Number of spliting parts for each image. + **/ + CImgList& split(const char axis, const int nb=-1) { return get_split(axis,nb).move_to(*this); } - CImgList get_split(const char axis, const int nb=0) const { + //! Return a list where each image has been split along the specified axis \newinstance. + CImgList get_split(const char axis, const int nb=-1) const { CImgList res; cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U); return res; } - //! Insert image \p img at the end of the list (STL-compliant name). + //! Insert image at the end of the list. + /** + \param img Image to insert. + **/ template CImgList& push_back(const CImg& img) { return insert(img); } - //! Insert image \p img at the front of the list (STL-compliant name). + //! Insert image at the front of the list. + /** + \param img Image to insert. + **/ template CImgList& push_front(const CImg& img) { return insert(img,0); } - //! Insert list \p list at the end of the current list (STL-compliant name). + //! Insert list at the end of the current list. + /** + \param list List to insert. + **/ template CImgList& push_back(const CImgList& list) { return insert(list); } - //! Insert list \p list at the front of the current list (STL-compliant name). + //! Insert list at the front of the current list. + /** + \param list List to insert. + **/ template CImgList& push_front(const CImgList& list) { return insert(list,0); } - //! Remove last element of the list (STL-compliant name). + //! Remove last image. + /** + **/ CImgList& pop_back() { - return remove(_width-1); + return remove(_width - 1); } - //! Remove first element of the list (STL-compliant name). + //! Remove first image. + /** + **/ CImgList& pop_front() { return remove(0); } - //! Remove the element pointed by iterator \p iter (STL-compliant name). + //! Remove image pointed by iterator. + /** + \param iter Iterator pointing to the image to remove. + **/ CImgList& erase(const iterator iter) { - return remove(iter-_data); + return remove(iter - _data); } //@} @@ -40608,31 +56431,50 @@ namespace cimg_library { //@{ //---------------------------------- - //! Simple interface to select sub-lists or single images in a list. + //! 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 get_select(CImgDisplay &disp, const bool feature_type=true, - const char axis='x', const float align=0) const { - return _get_select(disp,0,feature_type,axis,align,0,false,false,false); + 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 get_select(const char *const title, const bool feature_type=true, - const char axis='x', const float align=0) const { + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; - return _get_select(disp,title,feature_type,axis,align,0,false,false,false); + return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false); } - CImg _get_select(CImgDisplay &disp, const char *const title, const bool feature_type, - const char axis, const float align, - const unsigned int orig, const bool resize_disp, - const bool exit_on_rightbutton, const bool exit_on_wheel) const { + CImg _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.", + "select(): Empty instance.", cimglist_instance); // Create image correspondence table and get list dimensions for visualization. CImgList _indices; unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0; - cimglist_for(*this,l) if (_data[l]) { + cimglist_for(*this,l) { const CImg& img = _data[l]; const unsigned int w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), @@ -40660,7 +56502,7 @@ namespace cimg_library { bool old_is_resized = disp.is_resized(); disp._normalization = 0; disp.show().set_key(0); - const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; // Enter event loop. CImg visu0, visu; @@ -40669,6 +56511,7 @@ namespace cimg_library { 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. @@ -40676,36 +56519,40 @@ namespace cimg_library { 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; - if (axis=='x') for (unsigned int x = 0; x - &src = _data[ind], - _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg(), - &img2d = _img2d?_img2d:src; - CImg res = old_normalization==1?CImg(img2d.get_normalize(0,255)):CImg(img2d); - if (res._spectrum>3) res.channels(0,2); + const CImg onexone(1,1,1,1,(T)0); + if (axis=='x') + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4)) + cimglist_for(*this,ind) { + unsigned int x0 = 0; + while (x0 &src = _data[ind]?_data[ind]:onexone; + CImg 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(x - x0,cimg::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100); + 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,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 for (unsigned int y = 0; y - &src = _data[ind], - _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg(), - &img2d = _img2d?_img2d:src; - CImg res = old_normalization==1?CImg(img2d.get_normalize(0,255)):CImg(img2d); - if (res._spectrum>3) res.channels(0,2); + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4)) + cimglist_for(*this,ind) { + unsigned int y0 = 0; + while (y0 &src = _data[ind]?_data[ind]:onexone; + CImg 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(cimg::max(32U,w*disp._width/max_width),y - y0,1,res._spectrum==1?3:-100); - positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width()-res.width())); + 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; @@ -40718,18 +56565,25 @@ namespace cimg_library { if (!visu || oindice0!=indice0 || oindice1!=indice1) { if (indice0>=0 && indice1>=0) { visu.assign(visu0,false); - const int indm = cimg::min(indice0,indice1), indM = cimg::max(indice0,indice1); + 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); + 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,0x55555555); + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), + foreground_color,0.9f,0xAAAAAAAA); } - const int yt = (int)text_down?visu.height()-13:0; - if (is_clicked) visu.draw_text(0,yt," Images %u - %u, Size = %u",foreground_color,background_color,0.7f,13, + const int yt = (int)text_down?visu.height() - 13:0; + if (is_clicked) visu.draw_text(0,yt," Images #%u - #%u, Size = %u", + foreground_color,background_color,0.7f,13, orig + indm,orig + indM,indM - indm + 1); - else visu.draw_text(0,yt," Image %u",foreground_color,background_color,0.7f,13, - orig + indice0); + else visu.draw_text(0,yt," Image #%u (%u,%u,%u,%u)",foreground_color,background_color,0.7f,13, + orig + indice0, + _data[indice0]._width, + _data[indice0]._height, + _data[indice0]._depth, + _data[indice0]._spectrum); update_display = true; } else visu.assign(); } @@ -40762,23 +56616,28 @@ namespace cimg_library { if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; } if (disp.wheel() && exit_on_wheel) is_selected = true; + CImg 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). + 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_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_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()) { @@ -40787,44 +56646,53 @@ namespace cimg_library { } break; case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); if (visu0) { - visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp); + (+visu0).draw_text(0,0," Saving snapshot... ", + foreground_color,background_color,0.7f,13).display(disp); visu0.save(filename); - visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp); + (+visu0).draw_text(0,0," Snapshot '%s' saved. ", + foreground_color,background_color,0.7f,13,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; - char filename[32] = { 0 }; std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif - if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); - visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp); + (+visu0).draw_text(0,0," Saving instance... ", + foreground_color,background_color,0.7f,13).display(disp); save(filename); - visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp); + (+visu0).draw_text(0,0," Instance '%s' saved. ", + foreground_color,background_color,0.7f,13,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; }} + 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 res(1,2,1,1,-1); - if (is_selected) { if (feature_type) res.fill(cimg::min(indice0,indice1),cimg::max(indice0,indice1)); else res.fill(indice0); } + 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; @@ -40832,16 +56700,28 @@ namespace cimg_library { return res; } - //! Load an image list from a file. + //! Load a list from a file. + /** + \param filename Filename to read data from. + **/ CImgList& load(const char *const filename) { if (!filename) throw CImgArgumentException(_cimglist_instance - "load() : Specified filename is (null).", + "load(): Specified filename is (null).", cimglist_instance); + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg 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; + cimg::exception_mode(0); + bool is_loaded = true; try { #ifdef cimglist_load_plugin cimglist_load_plugin(filename); @@ -40872,6 +56752,7 @@ namespace cimg_library { #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); @@ -40887,59 +56768,88 @@ namespace cimg_library { !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_ffmpeg(filename); + !cimg::strcasecmp(ext,"mpeg")) load_video(filename); else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); - else throw CImgIOException("CImgList<%s>::load()", - pixel_type()); - } catch (CImgIOException& e) { - if (!cimg::strncasecmp(e.what(),"cimg::fopen()",13)) { - cimg::exception_mode() = omode; + 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 = std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); throw CImgIOException(_cimglist_instance - "load() : Failed to open file '%s'.", + "load(): Failed to open file '%s'.", cimglist_instance, filename); - } else try { - assign(1); + } + + const char *const f_type = cimg::ftype(file,filename); + std::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 (CImgException&) { + } catch (CImgIOException&) { + cimg::exception_mode(omode); throw CImgIOException(_cimglist_instance - "load() : Failed to recognize format of file '%s'.", + "load(): Failed to recognize format of file '%s'.", cimglist_instance, filename); } } - cimg::exception_mode() = omode; + cimg::exception_mode(omode); return *this; } + //! Load a list from a file \newinstance. static CImgList get_load(const char *const filename) { return CImgList().load(filename); } - //! Load an image list from a .cimg file. + //! Load a list from a .cimg file. + /** + \param filename Filename to read data from. + **/ CImgList& load_cimg(const char *const filename) { return _load_cimg(0,filename); } + //! Load a list from a .cimg file \newinstance. static CImgList get_load_cimg(const char *const filename) { return CImgList().load_cimg(filename); } - //! Load an image list from a .cimg file. + //! Load a list from a .cimg file. + /** + \param file File to read data from. + **/ CImgList& load_cimg(std::FILE *const file) { return _load_cimg(file,0); } + //! Load a list from a .cimg file \newinstance. static CImgList get_load_cimg(std::FILE *const file) { return CImgList().load_cimg(file); } @@ -40950,16 +56860,16 @@ namespace cimg_library { Bytef *const cbuf = new Bytef[csiz]; \ cimg::fread(cbuf,csiz,nfile); \ raw.assign(W,H,D,C); \ - unsigned long destlen = (unsigned long)raw.size()*sizeof(Tss); \ + uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \ uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ delete[] cbuf; \ - const Tss *ptrs = raw._data; \ - for (unsigned int off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \ + 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.", \ + "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \ cimglist_instance, \ filename?filename:"(FILE*)"); #endif @@ -40967,26 +56877,28 @@ namespace cimg_library { #define _cimg_load_cimg_case(Ts,Tss) \ if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l = 0; l=0) tmp[j++] = (char)i; tmp[j] = 0; \ + 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 = std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&C,&csiz))<4) \ + 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'", \ + "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 raw; \ CImg &img = _data[l]; \ - img.assign(W,H,D,C); \ - T *ptrd = img._data; \ if (err==5) _cimgz_load_cimg_case(Tss) \ - else for (long to_read = (long)img.size(); to_read>0; ) { \ - raw.assign(cimg::min(to_read,cimg_iobuffer)); \ - cimg::fread(raw._data,raw._width,nfile); \ - if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \ - to_read-=raw._width; \ - const Tss *ptrs = raw._data; \ - for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + 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; \ + } \ } \ } \ } \ @@ -40995,23 +56907,26 @@ namespace cimg_library { if (!filename && !file) throw CImgArgumentException(_cimglist_instance - "load_cimg() : Specified filename is (null).", + "load_cimg(): Specified filename is (null).", cimglist_instance); - const int cimg_iobuffer = 12*1024*1024; + const ulongT cimg_iobuffer = (ulongT)24*1024*1024; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool loaded = false, endian = cimg::endianness(); - char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 }; - unsigned int j, err, N = 0, W, H, D, C, csiz; - int i; + CImg 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!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; - } while (*tmp=='#' && i!=EOF); - err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); + 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'.", + "load_cimg(): CImg header not found in file '%s'.", cimglist_instance, filename?filename:"(FILE*)"); } @@ -41028,104 +56943,140 @@ namespace cimg_library { _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",unsigned long); - _cimg_load_cimg_case("ulong",unsigned long); - _cimg_load_cimg_case("long",long); + _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'.", + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", cimglist_instance, - str_pixeltype,filename?filename:"(FILE*)"); + str_pixeltype._data,filename?filename:"(FILE*)"); } if (!file) cimg::fclose(nfile); return *this; } - //! Load a sub-image list from a non compressed .cimg file. + //! 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& 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 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 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 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().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); } - //! Load a sub-image list from a non compressed .cimg file. + //! Load a sub-image list from a (non compressed) .cimg file \overloading. CImgList& 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 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 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 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().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); } CImgList& _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) { + 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 (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \ + 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'", \ + "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=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + if (l=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ else { \ const unsigned int \ - 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 raw(1 + nx1 - x0); \ - CImg &img = _data[l - n0]; \ - img.assign(1 + nx1 - x0,1 + ny1 - y0,1 + nz1 - z0,1 + nc1 - c0); \ + _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 raw(1 + _nx1 - nx0); \ + CImg &img = _data[l - nn0]; \ + img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \ T *ptrd = img._data; \ - const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ - if (skipvb) std::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) std::fseek(nfile,skipzb,SEEK_CUR); \ - for (unsigned int z = 1 + nz1 - z0; z; --z) { \ - const unsigned int skipyb = y0*W*sizeof(Tss); \ - if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \ - for (unsigned int y = 1 + ny1 - y0; y; --y) { \ - const unsigned int skipxb = x0*sizeof(Tss); \ - if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \ + 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 unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \ - if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \ + const ulongT 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) std::fseek(nfile,skipye,SEEK_CUR); \ + const ulongT 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) std::fseek(nfile,skipze,SEEK_CUR); \ + const ulongT 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) std::fseek(nfile,skipve,SEEK_CUR); \ + const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ } \ } \ } \ @@ -41134,33 +57085,41 @@ namespace cimg_library { if (!filename && !file) throw CImgArgumentException(_cimglist_instance - "load_cimg() : Specified filename is (null).", + "load_cimg(): Specified filename is (null).", cimglist_instance); - - if (n1%u] (%u,%u,%u,%u)->(%u,%u,%u,%u) for file '%s'.", - cimglist_instance, - n0,n1,x0,y0,z0,c0,x1,y1,z1,filename?filename:"(FILE*)"); + 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(); - char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 }; - unsigned int j, err, N, W, H, D, C; - int i; - j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; - err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); + CImg 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'.", + "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; - const unsigned int nn1 = n1>=N?N-1:n1; - assign(1+nn1-n0); + 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); @@ -41171,58 +57130,77 @@ namespace cimg_library { _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",unsigned long); - _cimg_load_cimg_case2("ulong",unsigned long); - _cimg_load_cimg_case2("long",long); + _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'.", + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", cimglist_instance, - str_pixeltype,filename?filename:"(FILE*)"); + str_pixeltype._data,filename?filename:"(FILE*)"); } if (!file) cimg::fclose(nfile); return *this; } - //! Load an image list from a PAR/REC (Philips) file. + //! Load a list from a PAR/REC (Philips) file. + /** + \param filename Filename to read data from. + **/ CImgList& load_parrec(const char *const filename) { if (!filename) throw CImgArgumentException(_cimglist_instance - "load_parrec() : Specified filename is (null).", + "load_parrec(): Specified filename is (null).", cimglist_instance); - char body[1024] = { 0 }, filenamepar[1024] = { 0 }, filenamerec[1024] = { 0 }; + CImg 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,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.rec",body); } - if (!std::strcmp(ext,"PAR")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.REC",body); } - if (!std::strcmp(ext,"rec")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.par",body); } - if (!std::strcmp(ext,"REC")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.PAR",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 st_slices; CImgList st_global; + CImg line(256); *line = 0; int err; - char line[256] = { 0 }; - do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (*line=='#' || *line=='.')); + do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.')); do { - unsigned int sn,sizex,sizey,pixsize; + 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,&sizex,&sizey,&ri,&rs,&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::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey,ri,rs,ss,0).move_to(st_slices); + CImg::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::vector(sizex,sizey,sn).move_to(st_global); + if (i==st_global._width) CImg::vector(size_x,size_y,sn).move_to(st_global); else { CImg &vec = st_global[i]; - if (sizex>vec[0]) vec[0] = sizex; - if (sizey>vec[1]) vec[1] = sizey; + 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; + st_slices[st_slices._width - 1][7] = (float)i; } } while (err==7); @@ -41238,29 +57216,29 @@ namespace cimg_library { const unsigned int sn = (unsigned int)vec[0] - 1, pixsize = (unsigned int)vec[1], - sizex = (unsigned int)vec[2], - sizey = (unsigned int)vec[3], + 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 buf(sizex,sizey); - cimg::fread(buf._data,sizex*sizey,file2); - if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey); + CImg 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& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; case 16 : { - CImg buf(sizex,sizey); - cimg::fread(buf._data,sizex*sizey,file2); - if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey); + CImg 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& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; case 32 : { - CImg buf(sizex,sizey); - cimg::fread(buf._data,sizex*sizey,file2); - if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey); + CImg 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& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; @@ -41268,7 +57246,7 @@ namespace cimg_library { cimg::fclose(file); cimg::fclose(file2); throw CImgIOException(_cimglist_instance - "load_parrec() : Unsupported %d-bits pixel type for file '%s'.", + "load_parrec(): Unsupported %d-bits pixel type for file '%s'.", cimglist_instance, pixsize,filename); } @@ -41277,431 +57255,576 @@ namespace cimg_library { cimg::fclose(file2); if (!_width) throw CImgIOException(_cimglist_instance - "load_parrec() : Failed to recognize valid PAR-REC data in file '%s'.", + "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 get_load_parrec(const char *const filename) { return CImgList().load_parrec(filename); } - //! Load an image sequence from a YUV file. + //! 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 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& load_yuv(const char *const filename, - const unsigned int sizex, const unsigned int sizey, + const unsigned int size_x, const unsigned int size_y, 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,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + return _load_yuv(0,filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); } + //! Load a list from a YUV image sequence file \newinstance. static CImgList get_load_yuv(const char *const filename, - const unsigned int sizex, const unsigned int sizey=1, + const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { - return CImgList().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + return CImgList().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); } - //! Load an image sequence from a YUV file. + //! Load a list from an image sequence YUV file \overloading. CImgList& load_yuv(std::FILE *const file, - const unsigned int sizex, const unsigned int sizey, + const unsigned int size_x, const unsigned int size_y, 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,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + return _load_yuv(file,0,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); } + //! Load a list from an image sequence YUV file \newinstance. static CImgList get_load_yuv(std::FILE *const file, - const unsigned int sizex, const unsigned int sizey=1, + const unsigned int size_x, const unsigned int size_y=1, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { - return CImgList().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + return CImgList().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); } CImgList& _load_yuv(std::FILE *const file, const char *const filename, - const unsigned int sizex, const unsigned int sizey, + const unsigned int size_x, const unsigned int size_y, 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).", + "load_yuv(): Specified filename is (null).", cimglist_instance); - if (sizex%2 || sizey%2) + if (size_x%2 || size_y%2) throw CImgArgumentException(_cimglist_instance - "load_yuv() : Invalid odd XY dimensions %ux%u in file '%s'.", + "load_yuv(): Invalid odd XY dimensions %ux%u in file '%s'.", cimglist_instance, - sizex,sizey,filename?filename:"(FILE*)"); - if (!sizex || !sizey) + size_x,size_y,filename?filename:"(FILE*)"); + if (!size_x || !size_y) throw CImgArgumentException(_cimglist_instance - "load_yuv() : Invalid sequence size (%u,%u) in file '%s'.", + "load_yuv(): Invalid sequence size (%u,%u) in file '%s'.", cimglist_instance, - sizex,sizey,filename?filename:"(FILE*)"); + size_x,size_y,filename?filename:"(FILE*)"); const unsigned int nfirst_frame = first_frame tmp(sizex,sizey,1,3), UV(sizex/2,sizey/2,1,2); + CImg tmp(size_x,size_y,1,3), UV(size_x/2,size_y/2,1,2); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - bool stopflag = false; + bool stop_flag = false; int err; if (nfirst_frame) { - err = std::fseek(nfile,nfirst_frame*(sizex*sizey + sizex*sizey/2),SEEK_CUR); + err = cimg::fseek(nfile,nfirst_frame*(size_x*size_y + size_x*size_y/2),SEEK_CUR); if (err) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimglist_instance - "load_yuv() : File '%s' doesn't contain frame number %u.", + "load_yuv(): File '%s' doesn't contain frame number %u.", cimglist_instance, filename?filename:"(FILE*)",nfirst_frame); } } unsigned int frame; - for (frame = nfirst_frame; !stopflag && frame<=nlast_frame; frame+=nstep_frame) { + for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) { tmp.fill(0); - // *TRY* to read the luminance part, do not replace by cimg::fread ! - err = (int)std::fread((void*)(tmp._data),1,(size_t)(tmp._width*tmp._height),nfile); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(tmp._data),1,(ulongT)tmp._width*tmp._height,nfile); if (err!=(int)(tmp._width*tmp._height)) { - stopflag = true; + 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.", + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", cimglist_instance, - filename?filename:"(FILE*)",sizex,sizey); + filename?filename:"(FILE*)",size_x,size_y); } else { UV.fill(0); - // *TRY* to read the luminance part, do not replace by cimg::fread ! + // *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())) { - stopflag = true; + 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.", + "load_yuv(): File '%s' contains incomplete data or given image dimensions (%u,%u) " + "are incorrect.", cimglist_instance, - filename?filename:"(FILE*)",sizex,sizey); + filename?filename:"(FILE*)",size_x,size_y); } else { cimg_forXY(UV,x,y) { const int x2 = x*2, y2 = y*2; - tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0); - tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1); + tmp(x2,y2,1) = tmp(x2 + 1,y2,1) = tmp(x2,y2 + 1,1) = tmp(x2 + 1,y2 + 1,1) = UV(x,y,0); + tmp(x2,y2,2) = tmp(x2 + 1,y2,2) = tmp(x2,y2 + 1,2) = tmp(x2 + 1,y2 + 1,2) = UV(x,y,1); } if (yuv2rgb) tmp.YCbCrtoRGB(); insert(tmp); - if (nstep_frame>1) std::fseek(nfile,(nstep_frame-1)*(sizex*sizey + sizex*sizey/2),SEEK_CUR); + if (nstep_frame>1) cimg::fseek(nfile,(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); } } } - if (stopflag && nlast_frame!=~0U && frame!=nlast_frame) + 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'.", + "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.", cimglist_instance, - nlast_frame,frame-1,filename?filename:"(FILE*)"); + nlast_frame,frame - 1,filename?filename:"(FILE*)"); if (!file) cimg::fclose(nfile); return *this; } - //! Load an image from a video file, using ffmpeg libraries. - // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net) - // I modified it afterwards for direct inclusion in the library core. - CImgList& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) { - if (!filename) + //! 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& 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_ffmpeg() : Specified filename is (null).", - cimglist_instance); - - const unsigned int - nfirst_frame = first_frame1) || (resume && (pixel_format || !pixel_format))) - throw CImgArgumentException(_cimglist_instance - "load_ffmpeg() : Unable to load sub-frames from file '%s' unless libffmpeg is enabled.", - cimglist_instance, - filename); - + "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 - const PixelFormat ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8; - avcodec_register_all(); - av_register_all(); - static AVFormatContext *format_ctx = 0; - static AVCodecContext *codec_ctx = 0; - static AVCodec *codec = 0; - static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame(); - static int vstream = 0; + static CvCapture *captures[32] = { 0 }; + static CImgList filenames(32); + static CImg positions(32,1,1,1,0); + static int last_used_index = -1; - if (resume) { - if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame) + // 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_ffmpeg() : Failed to resume loading of file '%s', due to unallocated FFMPEG structures.", - cimglist_instance, - filename); - } else { - // Open video file, find main video stream and codec. - if (format_ctx) av_close_input_file(format_ctx); - if (av_open_input_file(&format_ctx,filename,0,0,0)!=0) + "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_ffmpeg() : Failed to open file '%s'.", - cimglist_instance, - filename); - - if (!avframe || !converted_frame || av_find_stream_info(format_ctx)<0) { - av_close_input_file(format_ctx); format_ctx = 0; - return load_ffmpeg_external(filename); - } -#if cimg_verbosity>=3 - dump_format(format_ctx,0,0,0); -#endif - - // Special command : Return informations on main video stream. - // as a vector 1x4 containing : (nb_frames,width,height,fps). - if (!first_frame && !last_frame && !step_frame) { - for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream) - if (format_ctx->streams[vstream]->codec->codec_type==CODEC_TYPE_VIDEO) break; - if (vstream==(int)format_ctx->nb_streams) assign(); - else { - CImgList timestamps; - int nb_frames; - AVPacket packet; - // Count frames and store timestamps. - for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet)) - if (packet.stream_index==vstream) { - CImg::vector((double)packet.pts).move_to(timestamps); - ++nb_frames; - } - // Get frame with, height and fps. - const int - framew = format_ctx->streams[vstream]->codec->width, - frameh = format_ctx->streams[vstream]->codec->height; - const float - num = (float)(format_ctx->streams[vstream]->r_frame_rate).num, - den = (float)(format_ctx->streams[vstream]->r_frame_rate).den, - fps = num/den; - // Return infos as a list. - assign(2); - (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps); - (*this)[1] = (timestamps>'y'); - } - av_close_input_file(format_ctx); format_ctx = 0; - return *this; - } - - for (vstream = 0; vstream<(int)(format_ctx->nb_streams) && - format_ctx->streams[vstream]->codec->codec_type!=CODEC_TYPE_VIDEO; ) ++vstream; - if (vstream==(int)format_ctx->nb_streams) { - av_close_input_file(format_ctx); format_ctx = 0; - return load_ffmpeg_external(filename); - } - codec_ctx = format_ctx->streams[vstream]->codec; - codec = avcodec_find_decoder(codec_ctx->codec_id); - if (!codec) { - return load_ffmpeg_external(filename); - } - if (avcodec_open(codec_ctx,codec)<0) { // Open codec - return load_ffmpeg_external(filename); + "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::string(filename).move_to(filenames[index]); + positions[index] = 0; + cimg::mutex(9,0); + if (!captures[index]) { + filenames[index].assign(); + std::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); } } - // Read video frames - const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height); - uint8_t *const buffer = new uint8_t[numBytes]; - avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height); - const T foo = (T)0; - AVPacket packet; - for (unsigned int frame = 0, next_frame = nfirst_frame; frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) { - if (packet.stream_index==(int)vstream) { - int decoded = 0; -#if defined(AV_VERSION_INT) -#if LIBAVCODEC_VERSION_INTwidth,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width, - codec_ctx->height,ffmpeg_pixfmt,1,0,0,0); - sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize); - if (ffmpeg_pixfmt==PIX_FMT_RGB24) { - CImg next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true); - next_image._get_permute_axes("yzcx",foo).move_to(*this); - } else { - CImg next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true); - next_image._get_permute_axes("yzcx",foo).move_to(*this); + 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 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++); } - next_frame+=nstep_frame; - } - ++frame; + frame.move_to(*this); + ++pos; + + bool skip_failed = false; + for (unsigned int i = 1; inlast_frame) break; + cimg::mutex(9,0); + if (!src) break; } } - delete[] buffer; -#endif + + 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 } - static CImgList get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, const bool pixel_format=true) { - return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format); + //! Load an image from a video file, using OpenCV library \newinstance. + static CImgList 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().load_video(filename,first_frame,last_frame,step_frame); } - //! Load an image from a video file (MPEG,AVI) using the external tool 'ffmpeg'. + //! Load an image from a video file using the external tool 'ffmpeg'. + /** + \param filename Filename to read data from. + **/ CImgList& load_ffmpeg_external(const char *const filename) { if (!filename) throw CImgArgumentException(_cimglist_instance - "load_ffmpeg_external() : Specified filename is (null).", + "load_ffmpeg_external(): Specified filename is (null).", cimglist_instance); - - char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 }; + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + CImg command(1024), filename_tmp(256), filename_tmp2(256); std::FILE *file = 0; do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp); - if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file); + 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=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); } while (file); - cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%%6d.ppm",filetmp); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data); #if cimg_OS!=2 - cimg_snprintf(command,sizeof(command),"%s -i \"%s\" %s >/dev/null 2>&1",cimg::ffmpeg_path(),filename,filetmp2); + cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\" >/dev/null 2>&1", + cimg::ffmpeg_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp2)._system_strescape().data()); #else - cimg_snprintf(command,sizeof(command),"\"%s -i \"%s\" %s\" >NUL 2>&1",cimg::ffmpeg_path(),filename,filetmp2); + cimg_snprintf(command,command._width,"\"%s -i \"%s\" \"%s\"\" >NUL 2>&1", + cimg::ffmpeg_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp2)._system_strescape().data()); #endif cimg::system(command,0); const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode() = 0; + cimg::exception_mode(0); assign(); unsigned int i = 1; - for (bool stopflag = false; !stopflag; ++i) { - cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,i); + for (bool stop_flag = false; !stop_flag; ++i) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i); CImg img; - try { img.load_pnm(filetmp2); } - catch (CImgException&) { stopflag = true; } - if (img) { img.move_to(*this); std::remove(filetmp2); } + 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; + cimg::exception_mode(omode); if (is_empty()) throw CImgIOException(_cimglist_instance - "load_ffmpeg_external() : Failed to open file '%s' with external command 'ffmpeg'.", + "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 get_load_ffmpeg_external(const char *const filename) { return CImgList().load_ffmpeg_external(filename); } + //! Load gif file, using ImageMagick or GraphicsMagick's external tools. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gif_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_gif_external(): Specified filename is (null).", + cimglist_instance); + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + if (!_load_gif_external(filename,false)) + if (!_load_gif_external(filename,true)) + try { assign(CImg().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& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) { + CImg 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=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); +#if cimg_OS!=2 + if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\" >/dev/null 2>&1", + cimg::graphicsmagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\" >/dev/null 2>&1", + cimg::imagemagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); +#else + if (use_graphicsmagick) cimg_snprintf(command,command._width,"\"%s convert \"%s\" \"%s.png\"\" >NUL 2>&1", + cimg::graphicsmagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + else cimg_snprintf(command,command._width,"\"%s \"%s\" \"%s.png\"\" >NUL 2>&1", + cimg::imagemagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); +#endif + 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 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 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 get_load_gif_external(const char *const filename) { + return CImgList().load_gif_external(filename); + } + //! Load a gzipped list, using external tool 'gunzip'. + /** + \param filename Filename to read data from. + **/ CImgList& load_gzip_external(const char *const filename) { if (!filename) throw CImgIOException(_cimglist_instance - "load_gzip_external() : Specified filename is (null).", + "load_gzip_external(): Specified filename is (null).", cimglist_instance); - - char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + std::fclose(cimg::fopen(filename,"rb")); // Check if file exists. + CImg 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(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); - cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp); + cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); cimg::system(command); - if (!(file = std::fopen(filetmp,"rb"))) { + if (!(file = std_fopen(filename_tmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException(_cimglist_instance - "load_gzip_external() : Failed to open file '%s'.", + "load_gzip_external(): Failed to open file '%s'.", cimglist_instance, filename); } else cimg::fclose(file); - load(filetmp); - std::remove(filetmp); + load(filename_tmp); + std::remove(filename_tmp); return *this; } + //! Load a gzipped list, using external tool 'gunzip' \newinstance. static CImgList get_load_gzip_external(const char *const filename) { return CImgList().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 CImgList& load_off(const char *const filename, CImgList& primitives, CImgList& colors) { return get_load_off(filename,primitives,colors).move_to(*this); } + //! Load a 3d object from a .OFF file \newinstance. template static CImgList get_load_off(const char *const filename, CImgList& primitives, CImgList& colors) { return CImg().load_off(filename,primitives,colors)<'x'; } - //! Load a TIFF file. + //! 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& load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1) { + const unsigned int step_frame=1, + float *const voxel_size=0, + CImg *const description=0) { const unsigned int nfirst_frame = first_frame::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).", + "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); + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; + assign(1 + (nlast_frame - nfirst_frame)/nstep_frame); TIFFSetDirectory(tif,0); -#if cimg_verbosity>=3 - TIFFSetWarningHandler(0); - TIFFSetErrorHandler(0); -#endif - cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame); + cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,voxel_size,description); TIFFClose(tif); - } else throw CImgException(_cimglist_instance - "load_tiff() : Failed to open file '%s'.", - cimglist_instance, - filename); + } 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 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) { - return CImgList().load_tiff(filename,first_frame,last_frame,step_frame); + const unsigned int step_frame=1, + float *const voxel_size=0, + CImg *const description=0) { + return CImgList().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); } //@} @@ -41711,20 +57834,26 @@ namespace cimg_library { //@{ //---------------------------------- - //! Print informations about the list on the standard 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& 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?0:(msiz<8*1024*1024?1:2); - char _title[64] = { 0 }; - if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type()); - std::fprintf(cimg::output(),"%s: this = %p, size = %u [%u %s], data = (CImg<%s>*)%p", - title?title:_title,(void*)this,_width, + const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U; + CImg _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?"Kb":"Mb"), - pixel_type(),(void*)begin()); - if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end()-1)); + 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 }; @@ -41732,7 +57861,7 @@ namespace cimg_library { 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"); } + if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); } } std::fflush(cimg::output()); return *this; @@ -41740,49 +57869,63 @@ namespace cimg_library { //! Display the current CImgList instance in an existing CImgDisplay window (by reference). /** - This function displays the list images of the current CImgList instance into an existing CImgDisplay window. - Images of the list are concatenated in a single temporarly image for visualization purposes. + \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \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. - \param disp : reference to an existing CImgDisplay instance, where the current image list will be displayed. - \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'. - \param align : specify the alignment for image concatenation. - \return A reference to the current CImgList instance is returned. **/ const CImgList& display(CImgDisplay &disp, const char axis='x', const float align=0) const { - get_append(axis,align).display(disp); + disp.display(*this,axis,align); return *this; } //! Display the current CImgList instance in a new display window. /** - 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 concatenated 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. - \param title : specify the title of the opening display window. - \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'. - \param align : specify the alignment for image concatenation. - \return A reference to the current CImgList instance is returned. + \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& display(CImgDisplay &disp, const bool display_info, - const char axis='x', const float align=0) const { + 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,display_info,axis,align,0,true,is_exit); + 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 { 'x' | 'y' | 'z' | 'c' }. + \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& display(const char *const title=0, const bool display_info=true, - const char axis='x', const float align=0) const { + 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,display_info,axis,align,0,true,is_exit); + return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); } - const CImgList& _display(CImgDisplay &disp, const char *const title, const bool display_info, - const char axis, const float align, - const unsigned int orig, const bool is_first_call, bool &is_exit) const { + const CImgList& _display(CImgDisplay &disp, const char *const title, const CImgList *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.", + "display(): Empty instance.", cimglist_instance); if (!disp) { if (axis=='x') { @@ -41795,7 +57938,7 @@ namespace cimg_library { sum_width+=w; if (h>max_height) max_height = h; } - disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1); + 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) { @@ -41806,27 +57949,30 @@ namespace cimg_library { if (w>max_width) max_width = w; sum_height+=h; } - disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1); + disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1); } - if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + 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 dtitle = CImg::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). - 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,!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.set_title("%s",dtitle.data()); + 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 s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true); + const CImg s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true); disp_resize = true; - if (s[0]<0) { // No selections done. + 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. @@ -41835,30 +57981,70 @@ namespace cimg_library { if (!is_first_call && wheel<0) break; if (wheel>0 && _width>=4) { const unsigned int - delta = cimg::max(1U,(unsigned int)cimg::round(0.3*_width)), - ind0 = (unsigned int)cimg::max(0,s[0] - (int)delta), - ind1 = (unsigned int)cimg::min(width() - 1,s[0] + (int)delta); - if ((ind0!=0 || ind1!=_width-1) && ind1 - ind0>=3) get_shared_images(ind0,ind1)._display(disp,0,false,axis,align,orig + ind0,false,is_exit); + 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 sublist = get_shared_images(ind0,ind1); + CImgList 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) get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,orig+s[0],false,is_exit); + } else if (s[0]!=0 || s[1]!=width() - 1) { + const CImgList sublist = get_shared_images(s[0],s[1]); + CImgList 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; } - //! Save an image list into a file. + // [internal] Return string to describe display title. + CImg __display() const { + CImg res, str; + cimglist_for(*this,l) { + CImg::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(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. /** - Depending on the extension of the given filename, a file format is chosen for the output 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& save(const char *const filename, const int number=-1) const { + const CImgList& 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).", + "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); - char nfilename[1024] = { 0 }; - const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename; + CImg 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 @@ -41899,27 +58085,36 @@ namespace cimg_library { !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_ffmpeg(fn); + !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,l); } + 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 a CImgList can be saved as one single file. + //! 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") || @@ -41938,11 +58133,13 @@ namespace cimg_library { !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") || @@ -41952,339 +58149,77 @@ namespace cimg_library { return false; } - //! Save an image sequence, using FFMPEG library. - // This piece of code has been originally written by David. G. Starkweather. - const CImgList& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int fps=25, const unsigned int bitrate=2048) const { - if (!filename) - throw CImgArgumentException(_cimglist_instance - "save_ffmpeg() : Specified filename is (null).", - cimglist_instance); - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "save_ffmpeg() : Empty instance, for file '%s'.", - cimglist_instance, - filename); - if (!fps) - throw CImgArgumentException(_cimglist_instance - "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.", - cimglist_instance, - filename); + //! 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& save_gif_external(const char *const filename, const float fps=25, + const unsigned int nb_loops=0) { + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; + std::FILE *file = 0; - const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame; - if (first_frame>=_width || nlast_frame>=_width) - throw CImgArgumentException(_cimglist_instance - "save_ffmpeg() : Out of range specified frames [%u,%u], for file '%s'.", - cimglist_instance, - first_frame,last_frame,filename); - - for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[0])) - throw CImgInstanceException(_cimglist_instance - "save_ffmpeg() : Invalid instance dimensions, for file '%s'.", - cimglist_instance, - filename); - -#ifndef cimg_use_ffmpeg - return save_ffmpeg_external(filename,first_frame,last_frame,"mpeg2video",fps,bitrate); +#ifdef cimg_use_png +#define _cimg_save_gif_ext "png" #else - avcodec_register_all(); - av_register_all(); - const int - frame_dimx = _data[first_frame].width(), - frame_dimy = _data[first_frame].height(), - frame_dimv = _data[first_frame].spectrum(); - if (frame_dimv!=1 && frame_dimv!=3) - throw CImgInstanceException(_cimglist_instance - "save_ffmpeg() : Image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels, for file '%s'.", - cimglist_instance, - _data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum,_data,filename); - - PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P; - PixelFormat src_pxl_fmt = (frame_dimv==3)?PIX_FMT_RGB24:PIX_FMT_GRAY8; - - int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now). - AVOutputFormat *fmt = 0; -#if defined(AV_VERSION_INT) -#if LIBAVFORMAT_VERSION_INT::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); + } - AVFormatContext *oc = 0; -#if defined(AV_VERSION_INT) -#if LIBAVFORMAT_VERSION_INT::string(command).move_to(filenames,0); + cimg_snprintf(command,command._width,"\"%s\" >/dev/null 2>&1", + CImg::string(filename)._system_strescape().data()); + CImg::string(command).move_to(filenames); #else - oc = avformat_alloc_context(); + cimg_snprintf(command,command._width,"\"%s -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)std::max(0.0f,cimg::round(100/fps)),nb_loops); + CImg::string(command).move_to(filenames,0); + cimg_snprintf(command,command._width,"\"%s\"\" >NUL 2>&1", + CImg::string(filename)._system_strescape().data()); + CImg::string(command).move_to(filenames); #endif -#else - oc = av_alloc_format_context(); -#endif - if (!oc) // Failed to allocate format context. + CImg _command = filenames>'x'; + cimg_for(_command,p,char) if (!*p) *p = ' '; + _command.back() = 0; + + cimg::system(_command); + file = std_fopen(filename,"rb"); + if (!file) throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to allocate FFMPEG structure for format context, for file '%s'.", + "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.", cimglist_instance, filename); - - AVCodec *codec = 0; - AVFrame *picture = 0; - AVFrame *tmp_pict = 0; - oc->oformat = fmt; - std::sprintf(oc->filename,"%s",filename); - - // Add video stream. - int stream_index = 0; - AVStream *video_str = 0; - if (fmt->video_codec!=CODEC_ID_NONE) { - video_str = av_new_stream(oc,stream_index); - if (!video_str) { // Failed to allocate stream. - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to allocate FFMPEG structure for video stream, for file '%s'.", - cimglist_instance, - filename); - } - } else { // No codec identified. - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to identify proper codec, for file '%s'.", - cimglist_instance, - filename); - } - - AVCodecContext *c = video_str->codec; - c->codec_id = fmt->video_codec; - c->codec_type = CODEC_TYPE_VIDEO; - c->bit_rate = 1024*bitrate; - c->width = frame_dimx; - c->height = frame_dimy; - c->time_base.num = 1; - c->time_base.den = fps; - c->gop_size = 12; - c->pix_fmt = dest_pxl_fmt; - if (c->codec_id==CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2; - if (c->codec_id==CODEC_ID_MPEG1VIDEO) c->mb_decision = 2; - - if (av_set_parameters(oc,0)<0) { // Parameters not properly set. - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Invalid parameters set for avcodec, for file '%s'.", - cimglist_instance, - filename); - } - - // Open codecs and alloc buffers. - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { // Failed to find codec. - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : No valid codec found for file '%s'.", - cimglist_instance, - filename); - } - if (avcodec_open(c,codec)<0) // Failed to open codec. - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to open codec for file '%s'.", - cimglist_instance, - filename); - - tmp_pict = avcodec_alloc_frame(); - if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame. - avcodec_close(video_str->codec); - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to allocate memory for file '%s'.", - cimglist_instance, - filename); - } - tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx; - tmp_pict->type = FF_BUFFER_TYPE_USER; - int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy); - uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size); - if (!tmp_buffer) { // Failed to allocate memory for tmp buffer. - av_free(tmp_pict); - avcodec_close(video_str->codec); - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to allocate memory for file '%s'.", - cimglist_instance, - filename); - } - - // Associate buffer with tmp_pict. - avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy); - picture = avcodec_alloc_frame(); - if (!picture) { // Failed to allocate picture frame. - av_free(tmp_pict->data[0]); - av_free(tmp_pict); - avcodec_close(video_str->codec); - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to allocate memory for file '%s'.", - cimglist_instance, - filename); - } - - int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy); - uint8_t *buffer = (uint8_t*)av_malloc(size); - if (!buffer) { // Failed to allocate picture frame buffer. - av_free(picture); - av_free(tmp_pict->data[0]); - av_free(tmp_pict); - avcodec_close(video_str->codec); - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to allocate memory for file '%s'.", - cimglist_instance, - filename); - } - - // Associate the buffer with picture. - avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy); - - // Open file. - if (!(fmt->flags&AVFMT_NOFILE)) { - if (url_fopen(&oc->pb,filename,URL_WRONLY)<0) - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to open file '%s'.", - cimglist_instance, - filename); - } - - if (av_write_header(oc)<0) - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to write header in file '%s'.", - cimglist_instance, - filename); - - double video_pts; - SwsContext *img_convert_context = 0; - img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt, - c->width,c->height,c->pix_fmt,sws_flags,0,0,0); - if (!img_convert_context) { // Failed to get swscale context. - // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb); - av_free(picture->data); - av_free(picture); - av_free(tmp_pict->data[0]); - av_free(tmp_pict); - avcodec_close(video_str->codec); - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to get conversion context for file '%s'.", - cimglist_instance, - filename); - } - int ret = 0, out_size; - uint8_t *video_outbuf = 0; - int video_outbuf_size = 1000000; - video_outbuf = (uint8_t*)av_malloc(video_outbuf_size); - if (!video_outbuf) { - // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb); - av_free(picture->data); - av_free(picture); - av_free(tmp_pict->data[0]); - av_free(tmp_pict); - avcodec_close(video_str->codec); - av_free(oc); - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to allocate memory, for file '%s'.", - cimglist_instance, - filename); - } - - // Loop through each desired image in list. - for (unsigned int i = first_frame; i<=nlast_frame; ++i) { - CImg currentIm = _data[i], red, green, blue, gray; - if (src_pxl_fmt==PIX_FMT_RGB24) { - red = currentIm.get_shared_channel(0); - green = currentIm.get_shared_channel(1); - blue = currentIm.get_shared_channel(2); - cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format. - tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y); - tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y); - tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y); - } - } else { - gray = currentIm.get_shared_channel(0); - cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y); - } - - if (video_str) video_pts = (video_str->pts.val * video_str->time_base.num)/(video_str->time_base.den); - else video_pts = 0.0; - if (!video_str) break; - if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break; - out_size = avcodec_encode_video(c,video_outbuf,video_outbuf_size,picture); - if (out_size>0) { - AVPacket pkt; - av_init_packet(&pkt); - pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base); - if (c->coded_frame->key_frame) pkt.flags|=PKT_FLAG_KEY; - pkt.stream_index = video_str->index; - pkt.data = video_outbuf; - pkt.size = out_size; - ret = av_write_frame(oc,&pkt); - } else if (out_size<0) break; - if (ret) break; // Error occured in writing frame. - } - - // Close codec. - if (video_str) { - avcodec_close(video_str->codec); - av_free(picture->data[0]); - av_free(picture); - av_free(tmp_pict->data[0]); - av_free(tmp_pict); - } - if (av_write_trailer(oc)<0) - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : Failed to write trailer for file '%s'.", - cimglist_instance, - filename); - - av_freep(&oc->streams[stream_index]->codec); - av_freep(&oc->streams[stream_index]); - if (!(fmt->flags&AVFMT_NOFILE)) { - /*if (url_fclose(oc->pb)<0) - throw CImgIOException(_cimglist_instance - "save_ffmpeg() : File '%s', failed to close file.", - cimglist_instance, - filename); - */ - } - av_free(oc); - av_free(video_outbuf); -#endif + else cimg::fclose(file); + cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]); return *this; } - // Save an image sequence into a YUV file (internal). - const CImgList& _save_yuv(std::FILE *const file, const char *const filename, const bool rgb2yuv) const { + const CImgList& _save_yuv(std::FILE *const file, const char *const filename, const bool is_rgb) const { if (!file && !filename) throw CImgArgumentException(_cimglist_instance - "save_yuv() : Specified filename is (null).", + "save_yuv(): Specified filename is (null).", cimglist_instance); - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "save_yuv() : Empty instance, for file '%s'.", - cimglist_instance, - filename?filename:"(FILE*)"); - + if (is_empty()) { cimg::fempty(file,filename); return *this; } if ((*this)[0].width()%2 || (*this)[0].height()%2) throw CImgInstanceException(_cimglist_instance - "save_yuv() : Invalid odd instance dimensions (%u,%u) for file '%s'.", + "save_yuv(): Invalid odd instance dimensions (%u,%u) for file '%s'.", cimglist_instance, (*this)[0].width(),(*this)[0].height(), filename?filename:"(FILE*)"); @@ -42292,46 +58227,49 @@ namespace cimg_library { std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); cimglist_for(*this,l) { CImg YCbCr((*this)[l]); - if (rgb2yuv) YCbCr.RGBtoYCbCr(); - cimg::fwrite(YCbCr._data,YCbCr._width*YCbCr._height,nfile); + if (is_rgb) YCbCr.RGBtoYCbCr(); + cimg::fwrite(YCbCr._data,(size_t)YCbCr._width*YCbCr._height,nfile); cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1), - YCbCr._width*YCbCr._height/2,nfile); + (size_t)YCbCr._width*YCbCr._height/2,nfile); } if (!file) cimg::fclose(nfile); return *this; } - //! Save an image sequence into a YUV file. - const CImgList& save_yuv(const char *const filename=0, const bool rgb2yuv=true) const { - return _save_yuv(0,filename,rgb2yuv); - } - - //! Save an image sequence into a YUV file. - const CImgList& save_yuv(std::FILE *const file, const bool rgb2yuv=true) const { - return _save_yuv(file,0,rgb2yuv); - } - - //! Save an image list into a .cimg file. + //! Save list as a YUV image sequence file. /** - A CImg RAW file is a simple uncompressed binary file that may be used to save list of CImg images. - \param filename : name of the output file. - \return A reference to the current CImgList instance is returned. + \param filename Filename to write data to. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. **/ - const CImgList& _save_cimg(std::FILE *const file, const char *const filename, const bool compression) const { + const CImgList& save_yuv(const char *const filename=0, const bool is_rgb=true) const { + return _save_yuv(0,filename,is_rgb); + } + + //! Save image sequence into a YUV file. + /** + \param file File to write data to. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(std::FILE *const file, const bool is_rgb=true) const { + return _save_yuv(file,0,is_rgb); + } + + const CImgList& _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).", + "save_cimg(): Specified filename is (null).", cimglist_instance); #ifndef cimg_use_zlib - if (compression) + if (is_compressed) cimg::warn(_cimglist_instance - "save_cimg() : Unable to save compressed data in file '%s' unless zlib is enabled, saving them uncompressed.", + "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); + 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& img = _data[l]; @@ -42340,30 +58278,26 @@ namespace cimg_library { CImg tmp; if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } const CImg& ref = cimg::endianness()?tmp:img; - bool compressed = false; - if (compression) { + bool failed_to_compress = true; + if (is_compressed) { #ifdef cimg_use_zlib - const unsigned long siz = sizeof(T)*ref.size(); - unsigned long csiz = siz + siz/100 + 16; + 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)) { + 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.", + "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.", cimglist_instance, filename?filename:"(FILE*)"); - - compressed = false; - } else { + else { std::fprintf(nfile," #%lu\n",csiz); cimg::fwrite(cbuf,csiz,nfile); delete[] cbuf; - compressed = true; + failed_to_compress = false; } -#else - compressed = false; #endif } - if (!compressed) { + if (failed_to_compress) { // Write in a non-compressed way. std::fputc('\n',nfile); cimg::fwrite(ref._data,ref.size(),nfile); } @@ -42373,17 +58307,24 @@ namespace cimg_library { return *this; } - //! Save an image list into a CImg file (RAW binary file + simple header) - const CImgList& save_cimg(std::FILE *file, const bool compress=false) const { - return _save_cimg(file,0,compress); + //! 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& save_cimg(const char *const filename, const bool is_compressed=false) const { + return _save_cimg(0,filename,is_compressed); } - //! Save an image list into a CImg file (RAW binary file + simple header) - const CImgList& save_cimg(const char *const filename, const bool compress=false) const { - return _save_cimg(0,filename,compress); + //! 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& save_cimg(std::FILE *file, const bool is_compressed=false) const { + return _save_cimg(file,0,is_compressed); } - // Insert the image instance into into an existing .cimg file, at specified coordinates. const CImgList& _save_cimg(std::FILE *const file, const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, @@ -42391,15 +58332,15 @@ namespace cimg_library { #define _cimg_save_cimg_case(Ts,Tss) \ if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l = 0; l0) { \ - if (l=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + if (l=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ else { \ const CImg& img = (*this)[l - n0]; \ const T *ptrs = img._data; \ @@ -42408,37 +58349,37 @@ namespace cimg_library { 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 raw(1+nx1-x0); \ + 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 raw(1 + nx1 - x0); \ const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ - if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \ + 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) std::fseek(nfile,skipzb,SEEK_CUR); \ + 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) std::fseek(nfile,skipyb,SEEK_CUR); \ + 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) std::fseek(nfile,skipxb,SEEK_CUR); \ + 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) std::fseek(nfile,skipxe,SEEK_CUR); \ + if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ } \ const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \ - if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \ + if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ } \ const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \ - if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \ + if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ } \ const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \ - if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ } \ } \ } \ @@ -42447,31 +58388,32 @@ namespace cimg_library { if (!file && !filename) throw CImgArgumentException(_cimglist_instance - "save_cimg() : Specified filename is (null).", + "save_cimg(): Specified filename is (null).", cimglist_instance); if (is_empty()) throw CImgInstanceException(_cimglist_instance - "save_cimg() : Empty instance, for file '%s'.", + "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(); - char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 }; - unsigned int j, err, N, W, H, D, C; - int i; - j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; - err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); + CImg 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'.", + "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 = cimg::min(N,n0+_width); + 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); @@ -42482,23 +58424,31 @@ namespace cimg_library { _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_long",unsigned long); - _cimg_save_cimg_case("ulong",unsigned long); - _cimg_save_cimg_case("long",long); + _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'.", + "save_cimg(): Unsupported data type '%s' for file '%s'.", cimglist_instance, - filename?filename:"(FILE*)",str_pixeltype); + 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& save_cimg(const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, @@ -42507,6 +58457,14 @@ namespace cimg_library { } //! 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& save_cimg(std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, @@ -42514,22 +58472,29 @@ namespace cimg_library { return _save_cimg(file,0,n0,x0,y0,z0,c0); } - // Create an empty .cimg file with specified dimensions (internal) 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 unsigned int siz = dx*dy*dz*dc*sizeof(T); + 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 (unsigned int off=siz; off; --off) std::fputc(0,nfile); + for (ulongT off = siz; off; --off) std::fputc(0,nfile); } if (!file) cimg::fclose(nfile); } - //! Create an empty .cimg file with specified dimensions. + //! 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, @@ -42537,7 +58502,15 @@ namespace cimg_library { return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc); } - //! Create an empty .cimg file with specified dimensions. + //! 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, @@ -42545,145 +58518,450 @@ namespace cimg_library { return _save_empty_cimg(file,0,nb,dx,dy,dz,dc); } - //! Save a file in TIFF format. - const CImgList& save_tiff(const char *const filename, const unsigned int compression=0) const { + //! 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& 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).", + "save_tiff(): Specified filename is (null).", cimglist_instance); - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "save_tiff() : Empty instance, for file '%s'.", - cimglist_instance, - filename); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + #ifndef cimg_use_tiff - if (_width==1) _data[0].save_tiff(filename,compression); + if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff); else cimglist_for(*this,l) { - char nfilename[1024] = { 0 }; + CImg nfilename(1024); cimg::number_filename(filename,l,6,nfilename); - _data[l].save_tiff(nfilename,compression); + _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff); } #else - TIFF *tif = TIFFOpen(filename,"w"); + 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& img = (*this)[l]; - if (img) { - if (img._depth==1) img._save_tiff(tif,dir++,compression); - else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++,compression); - } + cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description); } TIFFClose(tif); } else - throw CImgException(_cimglist_instance - "save_tiff() : Failed to open stream for file '%s'.", - cimglist_instance, - filename); + throw CImgIOException(_cimglist_instance + "save_tiff(): Failed to open stream for file '%s'.", + cimglist_instance, + filename); #endif return *this; } - - //! Save an image list as a gzipped file, using external tool 'gzip'. + //! Save list as a gzipped file, using external tool 'gzip'. + /** + \param filename Filename to write data to. + **/ const CImgList& save_gzip_external(const char *const filename) const { if (!filename) throw CImgIOException(_cimglist_instance - "save_gzip_external() : Specified filename is (null).", + "save_gzip_external(): Specified filename is (null).", cimglist_instance); - - char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 }; + CImg 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(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); - else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + 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=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file); + if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); if (is_saveable(body)) { - save(filetmp); - cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename); + save(filename_tmp); + cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); cimg::system(command); - file = std::fopen(filename,"rb"); + file = std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimglist_instance - "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.", + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", cimglist_instance, filename); else cimg::fclose(file); - std::remove(filetmp); + std::remove(filename_tmp); } else { - char nfilename[1024] = { 0 }; + CImg nfilename(1024); cimglist_for(*this,l) { cimg::number_filename(body,l,6,nfilename); - if (*ext) std::sprintf(nfilename + std::strlen(nfilename),".%s",ext); + if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); _data[l].save_gzip_external(nfilename); } } return *this; } - //! Save an image sequence using the external tool 'ffmpeg'. - const CImgList& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const char *const codec="mpeg2video", const unsigned int fps=25, const unsigned int bitrate=2048) const { + //! 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& 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 filenames(32); + static CImg 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:"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::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 &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 _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& 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).", + "save_ffmpeg_external(): Specified filename is (null).", cimglist_instance); - if (is_empty()) - throw CImgInstanceException(_cimglist_instance - "save_ffmpeg_external() : Empty instance, for file '%s'.", - cimglist_instance, - filename); + if (is_empty()) { cimg::fempty(0,filename); return *this; } - char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 }; + const char + *const ext = cimg::split_filename(filename), + *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video"; + + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; std::FILE *file = 0; - const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame; - if (first_frame>=_width || nlast_frame>=_width) - throw CImgArgumentException(_cimglist_instance - "save_ffmpeg_external() : Out of range specified frames [%u,%u] for file '%s'.", - cimglist_instance, - filename,first_frame,last_frame); - - for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[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'.", + "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.", cimglist_instance, filename); do { - cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); - cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp); - if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file); + 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=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); } while (file); - for (unsigned int l = first_frame; l<=nlast_frame; ++l) { - cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,l+1); - if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2); - else _data[l].save_pnm(filetmp2); + cimglist_for(*this,l) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1); + CImg::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); } #if cimg_OS!=2 - cimg_snprintf(command,sizeof(command),"ffmpeg -i %s_%%6d.ppm -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1",filetmp,codec,bitrate,fps,filename); + cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1", + cimg::ffmpeg_path(), + CImg::string(filename_tmp)._system_strescape().data(), + _codec,bitrate,fps, + CImg::string(filename)._system_strescape().data()); #else - cimg_snprintf(command,sizeof(command),"\"ffmpeg -i %s_%%6d.ppm -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1",filetmp,codec,bitrate,fps,filename); + cimg_snprintf(command,command._width,"\"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1", + cimg::ffmpeg_path(), + CImg::string(filename_tmp)._system_strescape().data(), + _codec,bitrate,fps, + CImg::string(filename)._system_strescape().data()); #endif cimg::system(command); - file = std::fopen(filename,"rb"); + file = std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimglist_instance - "save_ffmpeg_external() : Failed to save file '%s' with external command 'ffmpeg'.", + "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.", cimglist_instance, filename); else cimg::fclose(file); - cimglist_for(*this,lll) { cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,lll+1); std::remove(filetmp2); } + cimglist_for(*this,l) std::remove(filenames[l]); return *this; } + //! Serialize a CImgList instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg 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 stream; + CImg 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::string(tmpstr,false).move_to(stream); + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + CImg::string(tmpstr,false).move_to(stream); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& 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::string(tmpstr,false).move_to(stream); + CImg(cbuf,csiz).move_to(stream); + delete[] cbuf; + failed_to_compress = false; + } +#endif + } + if (failed_to_compress) { // Write in a non-compressed way. + CImg::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::string("\n",false).move_to(stream); + } + cimglist_apply(stream,unroll)('y'); + return stream>'y'; + } + + //! Unserialize a CImg serialized buffer into a CImgList list. + template + static CImgList get_unserialize(const CImg& buffer) { +#ifdef cimg_use_zlib +#define _cimgz_unserialize_case(Tss) { \ + Bytef *cbuf = (Bytef*)stream; \ + if (sizeof(t)!=1 || cimg::type::string()==cimg::type::string()) { \ + cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \ + for (ulongT i = 0; i raw(W,H,D,C); \ + uLongf destlen = raw.size()*sizeof(Tss); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ + if (!is_bytef) delete[] cbuf; \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + raw.move_to(img); \ + } +#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::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 &img = res._data[l]; \ + if (err==5) _cimgz_unserialize_case(Tss) \ + else { \ + if (sizeof(t)!=1) { \ + CImg raw(W*sizeof(Tss),H,D,C); \ + cimg_for(raw,p,unsigned char) *p = (unsigned char)*(stream++); \ + img.assign((Tss*)raw._data,W,H,D,C); \ + } else img.assign((Tss*)stream,W,H,D,C); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(img._data,img.size()); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (buffer.is_empty()) + throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).", + pixel_type()); + CImgList res; + const t *stream = buffer._data, *const estream = buffer._data + buffer.size(); + bool loaded = false, endian = cimg::endianness(), is_bytef = false; + CImg 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::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; + } + //@} //---------------------------------- // @@ -42691,171 +58969,1228 @@ namespace cimg_library { //@{ //---------------------------------- - //! Create an auto-cropped font (along the X axis) from a input font \p font. + //! Crop font along the X-axis. + /** + **/ CImgList& crop_font() { return get_crop_font().move_to(*this); } + //! Crop font along the X-axis \newinstance. + /** + **/ CImgList get_crop_font() const { CImgList res; cimglist_for(*this,l) { const CImg& letter = (*this)[l]; - int xmin = letter._width, xmax = 0; + int xmin = letter.width(), xmax = 0; cimg_forXY(letter,x,y) if (letter(x,y)) { if (xxmax) xmax = x; } if (xmin>xmax) CImg(letter._width,letter._height,1,letter._spectrum,0).move_to(res); - else letter.get_crop(xmin,0,xmax,letter._height-1).move_to(res); + else letter.get_crop(xmin,0,xmax,letter._height - 1).move_to(res); } res[' '].resize(res['f']._width,-100,-100,-100,0); - if (' '+256& font(const unsigned int font_height, const bool variable_size=true) { + static const CImgList& font(const unsigned int font_height, const bool is_variable_width=true) { + if (!font_height) return CImgList::const_empty(); + cimg::mutex(11); -#define _cimg_font(sx,sy) \ - if (!variable_size && (!font || font[0]._height!=sy)) font = _font(cimg::font##sx##x##sy,sx,sy,false); \ - if (variable_size && (!vfont || vfont[0]._height!=sy)) vfont = _font(cimg::font##sx##x##sy,sx,sy,true); \ - if (font_height==sy) return variable_size?vfont:font; \ - if (variable_size) { \ - if (cvfont && font_height==cvfont[0]._height) return cvfont; \ - cvfont = vfont; \ - cimglist_for(cvfont,l) \ - cvfont[l].resize(cimg::max(1U,cvfont[l]._width*font_height/cvfont[l]._height),font_height,-100,-100, \ - cvfont[0]._height>font_height?2:5); \ - return cvfont; \ - } else { \ - if (cfont && font_height==cfont[0]._height) return cfont; \ - cfont = font; \ - cimglist_for(cfont,l) \ - cfont[l].resize(cimg::max(1U,cfont[l]._width*font_height/cfont[l]._height),font_height,-100,-100, \ - cfont[0]._height>font_height?2:5); \ - return cfont; \ - } \ + // Decompress nearest base font data if needed. + static const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 }; + static const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 }, + data_Ms[] = { 86,79,57,47 }; + const unsigned int data_ind = font_height<=13U?0U:font_height<=23U?1U:font_height<=53U?2U:3U; + static CImg base_fonts[4]; + CImg &base_font = base_fonts[data_ind]; + if (!base_font) { + const unsigned int w = data_widths[data_ind], h = data_heights[data_ind], M = data_Ms[data_ind]; + base_font.assign(256*w,h); + const char *data_font = data_fonts[data_ind]; + unsigned char *ptrd = base_font; + const unsigned char *const ptrde = base_font.end(); - static CImgList font, vfont, cfont, cvfont; - if (!font_height) return CImgList::empty(); - if (font_height<=13) { _cimg_font(10,13); } // [1,13] -> ref 13 - if (font_height<=28) { _cimg_font(12,24); } // [14,28] -> ref 24 - if (font_height<=32) { _cimg_font(16,32); } // [29,32] -> ref 32 - _cimg_font(29,57); // [33,+inf] -> ref 57 - } - - static CImgList _font(const unsigned int *const font, const unsigned int w, const unsigned int h, const bool variable_size) { - CImgList res(256,w,h,1,1); - const unsigned int *ptr = font; - unsigned int m = 0, val = 0; - for (unsigned int y = 0; y>=1; if (!m) { m = 0x80000000; val = *(ptr++); } - CImg& img = res[x/w]; - unsigned int xm = x%w; - img(xm,y) = (T)((val&m)?1:0); + // Special case needed for 90x103 to avoid MS compiler limit with big strings. + CImg data90x103; + if (!data_font) { + ((CImg(cimg::_data_font90x103[0], + (unsigned int)std::strlen(cimg::_data_font90x103[0]),1,1,1,true), + CImg(cimg::_data_font90x103[1], + (unsigned int)std::strlen(cimg::_data_font90x103[1]) + 1,1,1,1,true))>'x'). + move_to(data90x103); + data_font = data90x103.data(); } - if (variable_size) res.crop_font(); - return res.insert(res); + + // Uncompress font data (decode RLE). + for (const char *ptrs = data_font; *ptrs; ++ptrs) { + const int c = (int)(*ptrs - M - 32), v = c>=0?255:0, n = c>=0?c:-c; + 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 fonts[16]; + static bool is_variable_widths[16] = { 0 }; + unsigned int ind = ~0U; + for (int i = 0; i<16; ++i) + if (!fonts[i] || (is_variable_widths[i]==is_variable_width && font_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)); + std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool)); + std::memset(fonts + (ind=15),0,sizeof(CImgList)); // Free a slot in cache for new font. + } + CImgList &font = fonts[ind]; + + // Render requested font. + if (!font) { + const unsigned int padding_x = font_height<33U?1U:font_height<53U?2U:font_height<103U?3U:4U; + is_variable_widths[ind] = is_variable_width; + font = base_font.get_split('x',256); + if (font_height!=font[0]._height) + cimglist_for(font,l) + font[l].resize(std::max(1U,font[l]._width*font_height/font[l]._height),font_height,-100,-100, + font[0]._height>font_height?2:5); + if (is_variable_width) font.crop_font(); + cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,1,1,0,0,0.5); + 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 1-D Fast Fourier Transform, along specified axis. + //! 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& 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", + "FFT(): Instance has more than 2 images", cimglist_instance); CImg::FFT(_data[0],_data[1],axis,invert); return *this; } + //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance. CImgList get_FFT(const char axis, const bool invert=false) const { return CImgList(*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& 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", + "FFT(): Instance has more than 2 images", cimglist_instance); CImg::FFT(_data[0],_data[1],invert); return *this; } + //! Compute a n-d Fast Fourier Transform \newinstance. CImgList get_FFT(const bool invert=false) const { return CImgList(*this,false).FFT(invert); } - //! Invert primitives orientation of a 3d object. + //! Reverse primitives orientations of a 3d object. + /** + **/ CImgList& reverse_object3d() { cimglist_for(*this,l) { CImg& 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; + 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 get_reverse_object3d() const { return (+*this).reverse_object3d(); } //@} - }; + }; // struct CImgList { ... /* - #--------------------------------------------- - # - # Completion of previously declared functions - # - #---------------------------------------------- + #--------------------------------------------- + # + # Completion of previously declared functions + # + #---------------------------------------------- */ namespace cimg { - //! Display a dialog box, where a user can click standard buttons. + // 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 (with wide character support on Windows). + inline std::FILE *win_fopen(const char *const path, const char *const mode) { +#if cimg_OS==2 + // Convert 'path' to a wide-character string. + int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); + if (!err) return std_fopen(path,mode); + CImg wpath(err); + err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err); + if (!err) return std_fopen(path,mode); + + // Convert 'mode' to a wide-character string. + err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0); + if (!err) return std_fopen(path,mode); + CImg wmode(err); + err = MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err); + if (!err) return std_fopen(path,mode); + return _wfopen(wpath,wmode); +#else + return std_fopen(path,mode); +#endif + } + + //! Get/set path to store temporary files. /** - Up to 6 buttons can be defined in the dialog window. - This function returns when a user clicked one of the button or closed the dialog window. - \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. - \param button3_label = Label of the 3rd button. - \param button4_label = Label of the 4th button. - \param button5_label = Label of the 5th button. - \param button6_label = Label of the 6th button. - \param logo = Logo image displayed at the left of the main message. This parameter is optional. - \param centering = Tell to center the dialog window on the screen. - \return The button number (from 0 to 5), or -1 if the dialog window has been closed by the user. - \note If a button text is set to 0, then the corresponding button (and the followings) won't appear in - the dialog box. At least one button is necessary. + \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=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ + } + static CImg 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 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=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 Program Files/ 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 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 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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + if (!path_found) { + std::strcpy(s_path,"./convert"); + if ((file=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 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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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=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 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=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=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=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=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=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 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=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=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 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=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=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 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=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=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 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=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=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 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=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=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 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=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=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 &sa = *(CImg*)a, &sb = *(CImg*)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 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 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 pattern, _path = CImg::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::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::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 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(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::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 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(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),_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 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 _header = header.get_split(CImg::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 ext = CImg::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=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 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,url); + else + cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),timeout,filename_local,url); + } else { + if (referer) + cimg_snprintf(command,command._width,"%s -e %s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,filename_local,url); + else + cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),filename_local,url); + } + cimg::system(command); + + if (!(file = 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,url); + else + cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),timeout,filename_local,url); + } 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,url); + else + cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),filename_local,url); + } + cimg::system(command); + + if (!(file = 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 = std_fopen(filename_local,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"%s.gz",filename_local); + std::rename(command,filename_local); + file = 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 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::max(); + const unsigned int + edays = (unsigned int)(dt/86400000.0), + ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0), + emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0), + esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0), + ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0); + 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 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.0f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); } + else if (size<1024*1024*1024LU) { + const float nsize = size/(1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Mio",nsize); + } else { const float nsize = size/(1024*1024*1024.0f); 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 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& logo, const bool centering = false) { + const CImg& 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,centering); - throw CImgIOException("cimg::dialog() : No display available."); + 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 - const unsigned char + 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 @@ -42868,11 +60203,11 @@ namespace cimg { if (button6_label) { CImg().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."); + 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 = cimg::max(bw,buttons[l]._width); bh = cimg::max(bh,buttons[l]._height); } + 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; @@ -42880,73 +60215,81 @@ namespace cimg { if (bh>48) bh = 48; CImg 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); + 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 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); + 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 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); + 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(cbutton).draw_image(1+(bw-buttons[ll].width())/2,1+(bh-buttons[ll].height())/2,buttons[ll]). + CImg(cbutton). + draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]). move_to(cbuttons); - CImg(sbutton).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]). + CImg(sbutton). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). move_to(sbuttons); - CImg(button).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]). + CImg(button). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). move_to(buttons[ll]); } CImg canvas; - if (msg) CImg().draw_text(0,0,"%s",black,gray,1,13,msg).resize(-100,-100,1,3).move_to(canvas); + if (msg) + ((CImg().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; + 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(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_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(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_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]); } + 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,centering?true:false); - if (centering) disp.move((CImgDisplay::screen_width() - disp.width())/2, - (CImgDisplay::screen_height() - disp.height())/2); - bool stopflag = false, refresh = false; + 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() && !stopflag) { + while (!disp.is_closed() && !stop_flag) { if (refresh) { - if (clicked>=0) CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); + if (clicked>=0) + CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); else { - if (selected>=0) CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); + if (selected>=0) + CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); else canvas.display(disp); } refresh = false; @@ -42958,24 +60301,24 @@ namespace cimg { 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)) { + 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) stopflag = true; + } else if (clicked>=0) stop_flag = true; if (disp.key()) { oselected = selected; switch (disp.key()) { - case cimg::keyESC : selected=-1; stopflag=true; break; - case cimg::keyENTER : if (selected<0) selected = 0; stopflag = true; break; + 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::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break; case cimg::keyARROWLEFT : - case cimg::keyARROWUP : selected = (selected+buttons._width-1)%buttons._width; break; + case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break; } disp.set_key(); if (selected!=oselected) refresh = true; @@ -42986,28 +60329,56 @@ namespace cimg { #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 centering) { + const bool is_centered) { return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, - CImg::logo40x38(),centering); + CImg::_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 empty; return empty.eval(expression,x,y,z,c); } + template + inline CImg::type> eval(const char *const expression, const CImg& xyzc) { + static const CImg empty; + return empty.eval(expression,xyzc); + } + // End of cimg:: namespace } // End of cimg_library:: namespace } -#ifdef _cimg_redefine_None -#define None 0 +//! 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)) @@ -43018,6 +60389,9 @@ namespace cimg { #ifdef _cimg_redefine_PI #define PI 3.141592653589793238462643383 #endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif #endif // Local Variables: diff --git a/Euclidean.hpp b/Euclidean.hpp index 2ba5d9f..d8cfb8e 100644 --- a/Euclidean.hpp +++ b/Euclidean.hpp @@ -19,7 +19,7 @@ public: * @param const int x Coordinate X * @param const int y Coordinate Y */ - Euclidean(const int x, const int y) : _x(x), _y(y) {} + Euclidean(const int x, const int y) : _x(x), _y(y), _distance(0) {} /** * @param const int x Coordinate X diff --git a/main.cpp b/main.cpp index 887437b..d4304fd 100644 --- a/main.cpp +++ b/main.cpp @@ -13,7 +13,7 @@ #include "Euclidean.hpp" -#define MAX_ITERATIONS 15 +#define MAX_ITERATIONS 3 // Variance delta #define DELTA 50 @@ -40,130 +40,108 @@ double sum(CImg img, int startedX, int startedY, int w) { CImg decompose(const CImg input) { - CImginputImg(input); - std::vector vectEMax, vectEMin; - /////////////////////////////////////////////////////////////////////////////// // Part 1: Finding minimas and maximas // /////////////////////////////////////////////////////////////////////////////// - CImg imgMax(inputImg.channel(0)); - CImg imgMin(inputImg.channel(0)); - - int xmin, xmax, ymin, ymax; - float min, max; - - for (int i = 0; i < inputImg.width(); i += SIZE) { - for (int j = 0; j < inputImg.height(); j += SIZE) { - + std::vector vectEMax, vectEMin; + CImg imgMax(input.width(), input.height()); + CImg imgMin(input.width(), input.height()); + for (int i = 0; i < input.width(); i += SIZE) + for (int j = 0; j < input.height(); j += SIZE) + { // Save max and min locations - xmax = i; - ymax = j; - xmin = i; - ymin = j; - - // save values - max = imgMax(i,j); - min = imgMin(i,j); - - Euclidean eMax(i, j); - Euclidean eMin(i, j); + int xmin=i, xmax=i, ymin=j, ymax=j; + float min = input(i,j), max = input(i,j); + imgMax(i,j) = input(i,j); + imgMin(i,j) = input(i,j); // SIZExSIZE - for (int k = i; k < i + SIZE; k++) { - for (int l = j; l < j + SIZE; l++) { - + for (int k = i; k < i + SIZE; k++) + for (int l = j; l < j + SIZE; l++) + { // Max - if ((imgMax(k, l) <= max) && (l != ymax || k != xmax)) { - imgMax(k, l) = 0; - } else if (l!=ymax || k!=xmax) { - max = imgMax(k, l); - imgMax(xmax,ymax) = 0; + if ((input(k, l) > max) && (l != ymax || k != xmax)) + { + imgMax(xmax, ymax) = 0; + max = input(k, l); + imgMax(k,l) = max; xmax = k; ymax = l; - - eMax.setX(k); - eMax.setY(l); } // Min - if ((imgMin(k, l) >= min) && (l != ymin || k != xmin)) { - imgMin(k, l) = 0; - } else if (l != ymax || k != xmax) { + if ((imgMin(k, l) < min) && (l != ymin || k != xmin)) + { + imgMax(xmax, ymax) = 0; min = imgMin(k, l); - imgMin(xmin, ymin) = 0; + imgMax(k,l) = max; xmin = k; ymin = l; - - eMin.setX(k); - eMin.setY(l); } } - } - vectEMax.push_back(eMax); - vectEMin.push_back(eMin); + vectEMax.push_back(Euclidean(xmax,ymax)); + vectEMin.push_back(Euclidean(xmin,ymin)); } - } // Array of Euclidean distance to the nearest non zero element std::vector::iterator it1, it2; - for (it1 = vectEMax.begin(); it1 != vectEMax.end(); it1++) { - for (it2 = it1 + 1; it2 != vectEMax.end(); it2++) { + for (it1 = vectEMax.begin(); it1 != vectEMax.end(); it1++) + for (it2 = it1 + 1; it2 != vectEMax.end(); it2++) + { double dist = (*it1).computeDistanceFrom(*it2); - if (0 == (*it1).getDistance() || dist < (*it1).getDistance()) { + if ((*it1).getDistance() == 0 || dist < (*it1).getDistance()) + { (*it1).setDistance(dist); (*it1).setNearest(*it2); } - if (0 == (*it2).getDistance() || dist < (*it2).getDistance()) { + if ((*it2).getDistance() == 0 || dist < (*it2).getDistance()) + { (*it2).setDistance(dist); (*it2).setNearest(*it1); } } - } - for (it1 = vectEMin.begin(); it1 != vectEMin.end(); it1++) { - for (it2 = it1 + 1; it2 != vectEMin.end(); it2++) { + for (it1 = vectEMin.begin(); it1 != vectEMin.end(); it1++) + for (it2 = it1 + 1; it2 != vectEMin.end(); it2++) + { double dist = (*it1).computeDistanceFrom(*it2); - if (0 == (*it1).getDistance() || dist < (*it1).getDistance()) { + if ((*it1).getDistance() == 0 || dist < (*it1).getDistance()) + { (*it1).setDistance(dist); (*it1).setNearest(*it2); } - if (0 == (*it2).getDistance() || dist < (*it2).getDistance()) { + if ((*it2).getDistance() == 0 || dist < (*it2).getDistance()) + { (*it2).setDistance(dist); (*it2).setNearest(*it1); } } - } // Calculate the window size - int wmax = 0; - for(unsigned int i = 0; i < vectEMin.size(); i++) { - double d = MAX(Euclidean::max(vectEMax), Euclidean::max(vectEMin)); - - wmax = (int)ceil(d); - if(wmax % 2 == 0) { - wmax++; - } - } - - CImg imgSource(inputImg.channel(0)); + double d = MAX(Euclidean::max(vectEMax), Euclidean::max(vectEMin)); + int wmax = 2*((int)d/2)+1; // Order filters with source image std::vector vectFilterMax, vectFilterMin; - - for(int unsigned i = 0; i < vectEMax.size(); i++) { + for(int unsigned i = 0; i < vectEMax.size(); i++) + { float max = 0; - for (int k = vectEMax[i].getX() - ((wmax - 1) / 2); k < vectEMax[i].getX() + ((wmax + 1) / 2); k++) { - for (int l = vectEMax[i].getY() - ((wmax - 1) / 2); l < vectEMax[i].getY() + ((wmax + 1) / 2); l++) { - if( (k >= 0 && k < imgSource.width()) && (l >= 0 && l < imgSource.height()) ) { - if (imgSource(k, l) > max) { - max = imgSource(k, l); + for (int k = vectEMax[i].getX() - ((wmax - 1) / 2); k < vectEMax[i].getX() + ((wmax + 1) / 2); k++) + { + for (int l = vectEMax[i].getY() - ((wmax - 1) / 2); l < vectEMax[i].getY() + ((wmax + 1) / 2); l++) + { + if( (k >= 0 && k < input.width()) && (l >= 0 && l < input.height()) ) + { + if (input(k, l) > max) + { + max = input(k, l); } } } @@ -171,13 +149,17 @@ CImg decompose(const CImg input) vectFilterMax.push_back(max); } - for(int unsigned i = 0; i < vectEMin.size(); i++) { + for(int unsigned i = 0; i < vectEMin.size(); i++) + { float min = 255; - for (int k = vectEMin[i].getX() - ((wmax - 1) / 2); k < vectEMin[i].getX() + ((wmax + 1) / 2); k++) { - for (int l = vectEMin[i].getY() - ((wmax - 1) / 2); l < vectEMin[i].getY() + ((wmax + 1) / 2); l++) { - if( (k >= 0 && k < imgSource.width()) && (l >= 0 && l < imgSource.height()) ) { - if (imgSource(k, l) < min) { - min = imgSource(k, l); + for (int k = vectEMin[i].getX() - ((wmax - 1) / 2); k <= vectEMin[i].getX() + ((wmax + 1) / 2); k++) + { + for (int l = vectEMin[i].getY() - ((wmax - 1) / 2); l < vectEMin[i].getY() + ((wmax + 1) / 2); l++) + { + if( (k >= 0 && k < input.width()) && (l >= 0 && l < input.height()) ) + { + if (input(k, l) < min) { + min = input(k, l); } } } @@ -185,55 +167,61 @@ CImg decompose(const CImg input) vectFilterMin.push_back(min); } - CImg newImgMax(imgMax.width(), imgMax.height()); - // Calculate the upper envelope - for(int unsigned i = 0; i < vectEMax.size(); i++) { - for (int k = vectEMax[i].getX() - ((wmax - 1) / 2); k < vectEMax[i].getX() + ((wmax + 1) / 2); k++) { - for (int l = vectEMax[i].getY() - ((wmax - 1) / 2); l < vectEMax[i].getY() + ((wmax + 1) / 2); l++) { - if ((k >= 0 && k < imgSource.width()) && (l >= 0 && l < imgSource.height())) { - if( imgMax(k, l) == 0 ) { + CImg newImgMax(imgMax.width(), imgMax.height()); + for(int unsigned i = 0; i < vectEMax.size(); i++) + { + for (int k = vectEMax[i].getX() - ((wmax - 1) / 2); k < vectEMax[i].getX() + ((wmax + 1) / 2); k++) + { + for (int l = vectEMax[i].getY() - ((wmax - 1) / 2); l < vectEMax[i].getY() + ((wmax + 1) / 2); l++) + { + if ((k >= 0 && k < input.width()) && (l >= 0 && l < input.height())) + { + if (imgMax(k, l) == 0) imgMax(k, l) = vectFilterMax[i]; - } - else { + else imgMax(k, l) = (int)((imgMax(k, l) + vectFilterMax[i]) / 2); - } } } } } // Smooth of the upper envelope - for (int k = 0; k < imgSource.width(); k++) { - for (int l = 0; l < imgSource.height(); l++) { - if( (k >= 0 && k < imgSource.width()) && (l >= 0 && l < imgSource.height()) ) { + for (int k = 0; k < input.width(); k++) + { + for (int l = 0; l < input.height(); l++) { + if( (k >= 0 && k < input.width()) && (l >= 0 && l < input.height()) ) + { newImgMax(k, l) = (int)sum(imgMax, k, l, wmax) / (wmax * wmax); } } } - CImg newImgMin(imgMin.width(), imgMin.height()); - // Calculate the lower envelope + CImg newImgMin(imgMin.width(), imgMin.height()); for(int unsigned i = 0; i < vectEMin.size(); i++) { - for (int k = vectEMin[i].getX() - ((wmax - 1) / 2); k < vectEMin[i].getX() + ((wmax + 1) / 2); k++) { - for (int l = vectEMin[i].getY() - ((wmax - 1) / 2); l < vectEMin[i].getY() + ((wmax + 1) / 2); l++) { - if( (k >= 0 && k < imgSource.width()) && (l >= 0 && l < imgSource.height()) ) { - if( imgMin(k, l) == 0 ) { + for (int k = vectEMin[i].getX() - ((wmax - 1) / 2); k < vectEMin[i].getX() + ((wmax + 1) / 2); k++) + { + for (int l = vectEMin[i].getY() - ((wmax - 1) / 2); l < vectEMin[i].getY() + ((wmax + 1) / 2); l++) + { + if( (k >= 0 && k < input.width()) && (l >= 0 && l < input.height()) ) + { + if( imgMin(k, l) == 0 ) imgMin(k, l) = vectFilterMin[i]; - } - else { + else imgMin(k, l) = (int)((imgMin(k, l) + vectFilterMin[i]) / 2); - } } } } } // Smooth of the lower envelope - for (int k = 0; k < imgSource.width(); k++) { - for (int l = 0; l < imgSource.height(); l++) { - if( (k >= 0 && k < imgSource.width()) && (l >= 0 && l < imgSource.height()) ) { + for (int k = 0; k < input.width(); k++) + { + for (int l = 0; l < input.height(); l++) + { + if( (k >= 0 && k < input.width()) && (l >= 0 && l < input.height()) ) + { newImgMin(k, l) = (int)sum(imgMin, k, l, wmax) / (wmax * wmax); } } @@ -244,19 +232,16 @@ CImg decompose(const CImg input) /////////////////////////////////////////////////////////////////////////////// // Calculate the Average - CImg imgMoyenne(inputImg.width(), inputImg.height()); - - for (int i = 0; i < inputImg.width(); i++) { - for (int j = 0; j < inputImg.height(); j++) { + CImg imgMoyenne(input.width(), input.height()); + for (int i = 0; i < input.width(); i++) + for (int j = 0; j < input.height(); j++) imgMoyenne(i, j) = (newImgMin(i, j) + newImgMax(i, j)) /2; - } - } /////////////////////////////////////////////////////////////////////////////// // Partie 3: Deletion // /////////////////////////////////////////////////////////////////////////////// - return inputImg - imgMoyenne; + return input - imgMoyenne; } /*******************************************************************************