From 27f1d9cace8459ed7026f82726cdf8fbac790e75 Mon Sep 17 00:00:00 2001 From: Good Guy Date: Mon, 29 Feb 2016 15:22:59 -0700 Subject: [PATCH] update greycstoration CImg.h (163->168) --- cinelerra-5.0/plugins/greycstoration/CImg.h | 8834 ++++++++++++------- 1 file changed, 5818 insertions(+), 3016 deletions(-) diff --git a/cinelerra-5.0/plugins/greycstoration/CImg.h b/cinelerra-5.0/plugins/greycstoration/CImg.h index 87f5187c..8d36a56d 100644 --- a/cinelerra-5.0/plugins/greycstoration/CImg.h +++ b/cinelerra-5.0/plugins/greycstoration/CImg.h @@ -5,7 +5,7 @@ # # 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://tschumperle.users.greyc.fr/ ) @@ -54,7 +54,7 @@ // Set version number of the library. #ifndef cimg_version -#define cimg_version 163 +#define cimg_version 168 /*----------------------------------------------------------- # @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -123,15 +124,28 @@ #pragma warning(disable:4996) #define _CRT_SECURE_NO_DEPRECATE 1 #define _CRT_NONSTDC_NO_DEPRECATE 1 -#define cimg_snprintf cimg::c99_snprintf -#define cimg_vsnprintf cimg::c99_vsnprintf #endif -#ifndef cimg_snprintf +// 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 @@ -139,6 +153,8 @@ #include #include #include +#include +#include #elif cimg_OS==2 #ifndef NOMINMAX #define NOMINMAX @@ -154,13 +170,26 @@ // Look for C++11 features #if !defined(cimg_use_cpp11) && __cplusplus>201100 -#define cimg_use_cpp11 +#define cimg_use_cpp11 1 #endif -#ifdef cimg_use_cpp11 +#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 #include #include #endif +// Configure the 'abort' signal handler (does nothing by default). +// A typical signal handler can be defined in your own source like this: +// Without OpenMP support: #define cimg_test_abort() if (is_abort) throw CImgAbortException("") +// +// or +// +// With OpenMP support: #define cimg_test_abort() if (!omp_get_thread_num() && is_abort) throw CImgAbortException("") +// +// where 'is_abort' is a boolean variable. +#ifndef cimg_test_abort +#define cimg_test_abort() +#endif + // Configure filename separator. // // Filename separator is set by default to '/', except for Windows where it is '\'. @@ -184,7 +213,11 @@ // // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals. #ifndef cimg_verbosity +#if cimg_OS==2 #define cimg_verbosity 2 +#else +#define cimg_verbosity 1 +#endif #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4) #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined. #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }). @@ -199,11 +232,7 @@ #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 @@ -469,26 +498,6 @@ extern "C" { #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 cimg_argument(pos) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv) -#define cimg_argument1(pos,s0) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,1,s0) -#define cimg_argument2(pos,s0,s1) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,2,s0,s1) -#define cimg_argument3(pos,s0,s1,s2) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,3,s0,s1,s2) -#define cimg_argument4(pos,s0,s1,s2,s3) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3) -#define cimg_argument5(pos,s0,s1,s2,s3,s4) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4) -#define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) \ - cimg_library_suffixed::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_suffixed::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_suffixed::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_suffixed::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8) // Macros to define and manipulate local neighborhoods. #define CImg_2x2(I,T) T I[4]; \ @@ -1255,7 +1264,7 @@ extern "C" { (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ (I[5] = (T)(img)(_n1##x,y,z,c)), \ (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ - x==--_n1##x); \ + x==--_n1##x); \ I[0] = I[1], I[1] = I[2], \ I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], \ @@ -2067,26 +2076,26 @@ namespace cimg_library_suffixed { // Declare cimg:: namespace. // This is an uncomplete namespace definition here. It only contains some - // necessary stuffs to ensure a correct declaration order of the classes and functions + // 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_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; - const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; - const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; - const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; - const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; - const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; - const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; - const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; - const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; - const char t_underscore[] = { 0x1b, '[', '4', '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_black = 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, @@ -2107,35 +2116,73 @@ namespace cimg_library_suffixed { // [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,16] is reserved by CImg. + // '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; - cimg::mutex(0); - if (is_set) mode = value; - cimg::mutex(0,0); + if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } return mode; } // 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 c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) { - int count = -1; + 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) count = _vsnprintf_s(str,size,_TRUNCATE,format,ap); - if (count==-1) count = _vscprintf(format,ap); + if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap); + if (result==-1) result = _vscprintf(format,ap); cimg::mutex(6,0); - return count; + return result; } - inline int c99_snprintf(char* str, size_t size, const char* format, ...) { - int count; - va_list ap; - va_start(ap, format); - count = c99_vsnprintf(str,size,format,ap); - va_end(ap); - return count; + + // 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 @@ -2161,11 +2208,38 @@ namespace cimg_library_suffixed { 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 (quiet mode). + - \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 c=0); } @@ -2179,9 +2253,12 @@ namespace cimg_library_suffixed { /** \par Overview - CImgException is the base class of all exceptions thrown by \CImg. + 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 derived classes can be: + These classes can be: + + - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal. + This is the only \c non-derived exception class. - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid. This is probably one of the most thrown exception by \CImg. @@ -2239,28 +2316,65 @@ namespace cimg_library_suffixed { **/ struct CImgException : public std::exception { #define _cimg_exception_err(etype,disp_flag) \ - std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,16384,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_suffixed::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; - CImgException() { _message = new char[16384]; *_message = 0; } - CImgException(const char *const format, ...) { - _message = new char[16384]; *_message = 0; _cimg_exception_err("CImgException",true); + CImgException() { _message = new char[1]; *_message = 0; } + CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); } + CImgException(const CImgException& e) { + const int 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 int 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 an invalid 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) { + const int 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 int 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 @@ -2269,18 +2383,24 @@ namespace cimg_library_suffixed { 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 { @@ -2493,10 +2613,22 @@ namespace cimg_library_suffixed { return !(val==val); #endif } - static double min() { return -1.7E308; } - static double max() { return 1.7E308; } - static double inf() { return max()*max(); } - static double nan() { const double val_nan = -std::sqrt(-1.0); return val_nan; } + 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 valmax()?max():val; } static const char* format() { return "%.16g"; } static double format(const double val) { return val; } @@ -2519,8 +2651,8 @@ namespace cimg_library_suffixed { return !(val==val); #endif } - static float min() { return -3.4E38f; } - static float max() { return 3.4E38f; } + 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 float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; } @@ -2528,6 +2660,32 @@ namespace cimg_library_suffixed { 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 valmax()?max():val; } + static const char* format() { return "%.16g"; } + static double format(const long double val) { return (double)val; } + }; + template struct superset { typedef T type; }; template<> struct superset { typedef unsigned char type; }; template<> struct superset { typedef char type; }; @@ -3011,7 +3169,7 @@ namespace cimg_library_suffixed { const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI // Define a 12x13 font (small size). - const char *const data_font12x13 = + static const char *const data_font12x13 = " .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w Fw mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxl" "wlwkwuwnwuynwuwTwlwlwtwnwtwnw my Qw +wlw b{ \\w Wx`xTw_w[wbxawSwkw nynwky])]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^=]*\\" @@ -3312,7 +3470,7 @@ namespace cimg_library_suffixed { " F]']2] +]']2^ D]']3_ E]']1] \"]']2^ 8] H"; // Define a 90x103 font (huge size). - const char *const _data_font90x103[] = { // Defined as an array to avoid MS compiler limit about constant string (65Kb). + static const char *const _data_font90x103[] = { // Defined as an array to avoid MS compiler limit about constant string (65Kb). // Start of first string. " " " " @@ -3852,7 +4010,7 @@ namespace cimg_library_suffixed { " D" }; // Define a 40x38 'danger' color logo (used by cimg::dialog()). - const unsigned char logo40x38[4576] = { + 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, @@ -3959,7 +4117,7 @@ namespace cimg_library_suffixed { return -1; #else #if cimg_OS==1 - const unsigned int l = std::strlen(command); + const unsigned int l = (unsigned int)std::strlen(command); if (l) { char *const ncommand = new char[l + 16]; std::strncpy(ncommand,command,l); @@ -4121,7 +4279,7 @@ namespace cimg_library_suffixed { return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000); #elif cimg_OS==2 SYSTEMTIME st_time; - GetSystemTime(&st_time); + GetLocalTime(&st_time); return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); #else return 0; @@ -4222,8 +4380,9 @@ namespace cimg_library_suffixed { _rand(seed,true); } - inline double rand() { - return cimg::_rand()/16777215.; + 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 @@ -4244,19 +4403,20 @@ namespace cimg_library_suffixed { std::srand(seed); } - //! Return a random variable between [0,1] with respect to an uniform distribution. + //! Return a random variable uniformely distributed between [val_min,val_max]. /** **/ - inline double rand() { - return (double)std::rand()/RAND_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 between [-1,1] with respect to an uniform distribution. + //! Return a random variable uniformely distributed between [0,val_max]. /** - **/ - inline double crand() { - return 1 - 2*cimg::rand(); + **/ + 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. @@ -4265,8 +4425,8 @@ namespace cimg_library_suffixed { inline double grand() { double x1, w; do { - const double x2 = 2*cimg::rand() - 1.0; - x1 = 2*cimg::rand() - 1.0; + 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); @@ -4298,6 +4458,10 @@ namespace cimg_library_suffixed { return (double)rol((long)a,n); } + inline double rol(const long double a, const unsigned int n=1) { + return (double)rol((long)a,n); + } + //! Bitwise-rotate value on the right. template inline T ror(const T& a, const unsigned int n=1) { @@ -4312,6 +4476,10 @@ namespace cimg_library_suffixed { return (double)ror((long)a,n); } + inline double ror(const long double a, const unsigned int n=1) { + return (double)ror((long)a,n); + } + //! Return absolute value of a value. template inline T abs(const T& a) { @@ -4494,13 +4662,6 @@ namespace cimg_library_suffixed { else { const double tmp = absa/absb; return absb==0?0:absb*std::sqrt(1.0 + tmp*tmp); } } - inline bool _is_self_expr(const char *expression) { - if (!expression || *expression=='>' || *expression=='<') return false; - for (const char *s = expression; *s; ++s) - if ((*s=='i' || *s=='j') && (s[1]=='(' || s[1]=='[')) return true; - return false; - } - //! Convert ascii character to lower case. inline char uncase(const char x) { return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a'); @@ -4520,7 +4681,7 @@ namespace cimg_library_suffixed { **/ inline double atof(const char *const str) { double x = 0, y = 1; - return str && std::sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; + return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; } //! Compare the first \p l characters of two C-strings, ignoring the case. @@ -4554,6 +4715,51 @@ namespace cimg_library_suffixed { return cimg::strncasecmp(str1,str2,1 + (l1='0' && *ns<='7') ++ns; + cimg_sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns; *nd = (char)val; break; case 'x' : - std::sscanf(++ns,"%x",&val); - while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns; + 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++); @@ -4628,20 +4835,20 @@ namespace cimg_library_suffixed { // Return string that identifies the running OS. inline const char *stros() { #if defined(linux) || defined(__linux) || defined(__linux__) - const char *const str = "Linux"; + static const char *const str = "Linux"; #elif defined(sun) || defined(__sun) - const char *const str = "Sun OS"; + static const char *const str = "Sun OS"; #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) - const char *const str = "BSD"; + static const char *const str = "BSD"; #elif defined(sgi) || defined(__sgi) - const char *const str = "Irix"; + static const char *const str = "Irix"; #elif defined(__MACOSX__) || defined(__APPLE__) - const char *const str = "Mac OS"; + static const char *const str = "Mac OS"; #elif defined(unix) || defined(__unix) || defined(__unix__) - const char *const str = "Generic Unix"; + static const char *const str = "Generic Unix"; #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ defined(WIN64) || defined(_WIN64) || defined(__WIN64__) - const char *const str = "Windows"; + static const char *const str = "Windows"; #else const char *const _str1 = std::getenv("OSTYPE"), @@ -4652,20 +4859,21 @@ namespace cimg_library_suffixed { } //! Return the basename of a filename. - inline const char* basename(const char *const s) { + 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,cimg_file_separator) + 1; + while (np>=s && (p=np)) np = std::strchr(np,separator) + 1; return p; } // Return a random filename. inline const char* filenamerand() { cimg::mutex(6); - static char randomid[9] = { 0 }; + 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; @@ -4726,7 +4934,7 @@ namespace cimg_library_suffixed { return errn; } - //! Check if a path is a directory + //! Check if a path is a directory. /** \param path Specified path to test. **/ @@ -4734,49 +4942,122 @@ namespace cimg_library_suffixed { if (!path || !*path) return false; #if cimg_OS==1 struct stat st_buf; - if (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode)) return true; + return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode)); #elif cimg_OS==2 - return GetFileAttributesA(path)&16; + const unsigned int res = (unsigned int)GetFileAttributesA(path); + return res==INVALID_FILE_ATTRIBUTES?false:(res&16); #endif - return false; } - //! Get/set path to store temporary files. + //! 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 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 + 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); + } +#else + 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 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 + 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 + 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 + cimg::mutex(6,0); + return res; + } + + // Get/set path to store temporary files. inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false); - //! Get/set path to the Program Files/ directory (Windows only). + // Get/set path to the Program Files/ directory (Windows only). #if cimg_OS==2 inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false); #endif - //! Get/set path to the ImageMagick's \c convert binary. + // Get/set path to the ImageMagick's \c convert binary. inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false); - //! Get/set path to the GraphicsMagick's \c gm binary. + // Get/set path to the GraphicsMagick's \c gm binary. inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false); - //! Get/set path to the XMedcon's \c medcon binary. + // 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. + // 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. + // 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. + // 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. + // 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. + // 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. + // 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) {} @@ -4797,64 +5078,11 @@ namespace cimg_library_suffixed { const char *const ext = cimg::split_filename(filename,body); if (*ext) cimg_snprintf(format,1024,"%%s_%%.%ud.%%s",digits); else cimg_snprintf(format,1024,"%%s_%%.%ud",digits); - std::sprintf(str,format,body,number,ext); + cimg_sprintf(str,format,body,number,ext); delete[] format; delete[] body; return str; } - //! 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 *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 *const header = new char[2048]; *header = 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; // 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 + 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. - char *const item = new char[1024]; *item = 0; - head = header; - while (head
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){ + 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){ + float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){ sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); } @@ -5327,6 +5540,7 @@ namespace cimg_library_suffixed { _cimg_create_ext_operators(long) _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) { @@ -5757,6 +5971,12 @@ namespace cimg_library_suffixed { return _empty.assign(); } + //! Return a reference to an empty display \const. + static const CImgDisplay& const_empty() { + static const CImgDisplay _empty; + return _empty; + } + #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,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, @@ -6146,7 +6366,7 @@ namespace cimg_library_suffixed { in fullscreen mode. **/ const char *title() const { - return _title; + return _title?_title:""; } //! Return width of the associated window. @@ -6383,6 +6603,10 @@ namespace cimg_library_suffixed { **/ template CImgDisplay& display(const CImgList& list, const char axis='x', const float align=0) { + 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); cimglist_for(list,l) { const CImg& img = list._data[l]; @@ -7087,7 +7311,7 @@ namespace cimg_library_suffixed { XEvent event; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); - if (!arg) for (;;) { + if (!arg) for ( ; ; ) { cimg_lock_display(); bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event); if (!event_flag) event_flag = XCheckMaskEvent(dpy, @@ -7348,7 +7572,7 @@ namespace cimg_library_suffixed { // 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)); @@ -7378,7 +7602,7 @@ namespace cimg_library_suffixed { XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals); if (vinfo && vinfo->red_maskblue_mask) cimg::X11_attr().is_blue_first = true; cimg::X11_attr().byte_order = ImageByteOrder(dpy); - XFree(vinfo); + XFree(vinfo); cimg_lock_display(); cimg::X11_attr().events_thread = new pthread_t; @@ -7407,8 +7631,8 @@ namespace cimg_library_suffixed { _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L); XSelectInput(dpy,_window, - ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | - EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); + ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | + EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); XStoreName(dpy,_window,_title?_title:" "); if (cimg::X11_attr().nb_bits==8) { @@ -7486,7 +7710,7 @@ namespace cimg_library_suffixed { // Remove display window from event thread list. unsigned int i; for (i = 0; i0)?nheight:(-nheight*height()/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; - 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); + 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); + } } + 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(); } - 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; _is_resized = false; - cimg_unlock_display(); if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2); if (force_redraw) return paint(); return *this; @@ -7643,13 +7870,15 @@ namespace cimg_library_suffixed { CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; - show(); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - 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; - cimg_unlock_display(); return paint(); } @@ -7666,7 +7895,7 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; Display *const dpy = cimg::X11_attr().display; cimg_lock_display(); - const char pix_data[8] = { 0 }; + 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); @@ -7698,7 +7927,7 @@ namespace cimg_library_suffixed { va_end(ap); 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; @@ -7766,16 +7995,16 @@ namespace cimg_library_suffixed { (*ptrd++) = (unsigned char)*(data1++); break; case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { - const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++); - (*ptrd++) = (R&0xf0) | (G>>4); - } break; + const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; default : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) { - const unsigned char + const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++); - (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); - } + (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } } if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); @@ -8087,7 +8316,7 @@ namespace cimg_library_suffixed { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; } - } + } } } cimg_unlock_display(); @@ -8220,7 +8449,9 @@ namespace cimg_library_suffixed { } break; case WM_PAINT : disp->paint(); - if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); + 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; @@ -8250,12 +8481,16 @@ namespace cimg_library_suffixed { disp->_mouse_x = disp->_mouse_y = -1; disp->_is_event = true; SetEvent(cimg::Win32_attr().wait_event); - if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); - } break; + 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; - while (ShowCursor(TRUE)<0); + cimg::mutex(15); + while (ShowCursor(TRUE)<0); + cimg::mutex(15,0); } break; case WM_LBUTTONDOWN : disp->set_button(1); @@ -8514,24 +8749,27 @@ namespace cimg_library_suffixed { 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 = (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; + 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(); } - _window_width = dimx; _window_height = dimy; _is_resized = false; if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2); if (force_redraw) return paint(); @@ -8574,19 +8812,22 @@ namespace cimg_library_suffixed { 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 = (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; + 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() { @@ -8602,7 +8843,7 @@ namespace cimg_library_suffixed { } 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; } @@ -8842,10 +9083,8 @@ namespace cimg_library_suffixed { - 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. @@ -8928,6 +9167,11 @@ namespace cimg_library_suffixed { typedef typename cimg::last::type longT; typedef typename cimg::last::type floatT; typedef typename cimg::last::type doubleT; +#if cimg_OS==2 + typedef typename cimg::last::type uptrT; // Unsigned integer type that can store a pointer. +#else + typedef typename cimg::last::type uptrT; +#endif //@} //--------------------------- @@ -9108,24 +9352,24 @@ namespace cimg_library_suffixed { 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 long _siz = (unsigned long)N; \ - if (_siz--) { \ - va_list ap; \ - va_start(ap,a1); \ - T *ptrd = (img)._data; \ - *(ptrd++) = (T)a0; \ - if (_siz--) { \ - *(ptrd++) = (T)a1; \ - for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ - } \ - va_end(ap); \ - } \ + unsigned long _siz = (unsigned long)N; \ + if (_siz--) { \ + va_list ap; \ + va_start(ap,a1); \ + T *ptrd = (img)._data; \ + *(ptrd++) = (T)a0; \ + if (_siz--) { \ + *(ptrd++) = (T)a1; \ + for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ + } \ + va_end(ap); \ + } \ } assign(size_x,size_y,size_z,size_c); _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int); } -#ifdef cimg_use_cpp11 +#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 //! 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, @@ -9154,7 +9398,7 @@ namespace cimg_library_suffixed { 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): + 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(); \ @@ -9169,7 +9413,7 @@ namespace cimg_library_suffixed { 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): + 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); @@ -9178,7 +9422,7 @@ namespace cimg_library_suffixed { template CImg(const unsigned int size_x, const unsigned int size_y, std::initializer_list values, - const bool repeat_values=true): + 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); @@ -9285,7 +9529,7 @@ namespace cimg_library_suffixed { \image html ref_constructor2.jpg **/ CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, - const char *const values, const bool repeat_values):_is_shared(false) { + const char *const values, const bool repeat_values):_is_shared(false) { const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c; if (siz) { _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; @@ -9374,7 +9618,8 @@ namespace cimg_library_suffixed { 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; } } @@ -9580,7 +9825,7 @@ namespace cimg_library_suffixed { // 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 ! -#ifdef cimg_use_cpp11 +#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 CImg(CImg&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { swap(img); } @@ -9610,13 +9855,13 @@ namespace cimg_library_suffixed { if (!siz) return assign(); const unsigned long curr_siz = size(); if (siz!=curr_siz) { - if (_is_shared) + if (_is_shared) throw CImgArgumentException(_cimg_instance "assign(): Invalid assignement request of shared instance from specified " "image (%u,%u,%u,%u).", cimg_instance, size_x,size_y,size_z,size_c); - else { + else { delete[] _data; try { _data = new T[siz]; } catch (...) { _width = _height = _depth = _spectrum = 0; _data = 0; @@ -9736,14 +9981,14 @@ namespace cimg_library_suffixed { if (!values || !siz) return assign(); if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); } else { - if (!_is_shared) { - if (values + siz<_data || values>=_data + size()) assign(); - else cimg::warn(_cimg_instance + if (!_is_shared) { + if (values + siz<_data || values>=_data + size()) assign(); + else cimg::warn(_cimg_instance "assign(): Shared image instance has overlapping memory.", cimg_instance); - } - _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; - _data = const_cast(values); + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; + _data = const_cast(values); } return *this; } @@ -9784,10 +10029,10 @@ namespace cimg_library_suffixed { unsigned int siz[4] = { 0,1,1,1 }, k = 0; CImg item(256); for (const char *s = dimensions; *s && k<4; ++k) { - if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>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; @@ -9928,6 +10173,12 @@ namespace cimg_library_suffixed { return _empty.assign(); } + //! Return a reference to an empty image \const. + static const CImg& const_empty() { + static const CImg _empty; + return _empty; + } + //@} //------------------------------------------ // @@ -9974,9 +10225,9 @@ namespace cimg_library_suffixed { const unsigned long off = (unsigned long)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]; @@ -10205,7 +10456,7 @@ namespace cimg_library_suffixed { CImg& operator+=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if (size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd + value); return *this; @@ -10224,14 +10475,21 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator+="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator+=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel { _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; @@ -10296,7 +10554,7 @@ namespace cimg_library_suffixed { CImg& operator++() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if (size()>=524288) #endif cimg_rof(*this,ptrd,T) ++*ptrd; return *this; @@ -10365,7 +10623,7 @@ namespace cimg_library_suffixed { CImg& operator-=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if (size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd - value); return *this; @@ -10380,23 +10638,30 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator-="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator-=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)(*ptrd - lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)(*ptrd - lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); ++ptrd; } @@ -10434,7 +10699,7 @@ namespace cimg_library_suffixed { CImg& operator--() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if (size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = *ptrd - (T)1; return *this; @@ -10505,7 +10770,7 @@ namespace cimg_library_suffixed { CImg& operator*=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=262144) +#pragma omp parallel for cimg_openmp_if (size()>=262144) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd * value); return *this; @@ -10520,23 +10785,30 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator*="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator*=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)(*ptrd * lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)(*ptrd * lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); ++ptrd; } @@ -10605,7 +10877,7 @@ namespace cimg_library_suffixed { img._width,img._height,img._depth,img._spectrum,img._data); CImg<_cimg_Tt> res(img._width,_height); #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>1024 && img.size()>1024) collapse(2) +#pragma omp 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; } @@ -10626,7 +10898,7 @@ namespace cimg_library_suffixed { CImg& operator/=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd / value); return *this; @@ -10641,23 +10913,30 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator/="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator/=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)(*ptrd / lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)(*ptrd / lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); ++ptrd; } @@ -10723,7 +11002,7 @@ namespace cimg_library_suffixed { CImg& operator%=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=16384) +#pragma omp parallel for cimg_openmp_if (size()>=16384) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value); return *this; @@ -10738,23 +11017,30 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator%="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator%=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::mod(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)cimg::mod(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } @@ -10822,7 +11108,7 @@ namespace cimg_library_suffixed { CImg& operator&=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value); return *this; @@ -10837,8 +11123,12 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator&="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator&=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); --ptrd; } @@ -10846,16 +11136,17 @@ namespace cimg_library_suffixed { cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); ++ptrd; } @@ -10923,7 +11214,7 @@ namespace cimg_library_suffixed { CImg& operator|=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value); return *this; @@ -10938,8 +11229,12 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator|="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator|=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); --ptrd; } @@ -10947,16 +11242,17 @@ namespace cimg_library_suffixed { cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); ++ptrd; } @@ -11026,7 +11322,7 @@ namespace cimg_library_suffixed { CImg& operator^=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value); return *this; @@ -11043,8 +11339,12 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator^="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator^=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); --ptrd; } @@ -11052,7 +11352,8 @@ namespace cimg_library_suffixed { cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel { _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; @@ -11131,7 +11432,7 @@ namespace cimg_library_suffixed { CImg& operator<<=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if (size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value); return *this; @@ -11146,23 +11447,30 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator<<="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator<<=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd << (int)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd << (int)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); ++ptrd; } @@ -11231,7 +11539,7 @@ namespace cimg_library_suffixed { CImg& operator>>=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if (size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value); return *this; @@ -11246,23 +11554,30 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator<<="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator<<=",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd >> (int)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd >> (int)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); ++ptrd; } @@ -11359,8 +11674,12 @@ namespace cimg_library_suffixed { cimg::exception_mode(0); bool is_equal = true; try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator<<="); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "operator<<=",base,0); const T *ptrs = *expression=='<'?end() - 1:_data; if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs--)==mp(x,y,z,c)); } @@ -12056,7 +12375,7 @@ namespace cimg_library_suffixed { **/ 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; + x = (int)fx - (fx>=0?0:1), nx = x + 1; const float dx = fx - x; const Tfloat @@ -13381,7 +13700,7 @@ namespace cimg_library_suffixed { // 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, + 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, @@ -13393,19 +13712,19 @@ namespace cimg_library_suffixed { // Check consistency of vertices. if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions. - if (error_message) std::sprintf(error_message, + 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 (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, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines %lu opacities", _width,primitives._width,(unsigned long)opacities.size()); return false; @@ -13420,7 +13739,7 @@ namespace cimg_library_suffixed { case 1 : { // Point. const unsigned int i0 = (unsigned int)primitive(0); if (i0>=_width) { - if (error_message) std::sprintf(error_message, + 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); @@ -13432,7 +13751,7 @@ namespace cimg_library_suffixed { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) std::sprintf(error_message, + 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); @@ -13445,7 +13764,7 @@ namespace cimg_library_suffixed { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) std::sprintf(error_message, + 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); @@ -13459,7 +13778,7 @@ namespace cimg_library_suffixed { i1 = (unsigned int)primitive(1), i2 = (unsigned int)primitive(2); if (i0>=_width || i1>=_width || i2>=_width) { - if (error_message) std::sprintf(error_message, + 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); @@ -13474,7 +13793,7 @@ namespace cimg_library_suffixed { 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, + 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); @@ -13482,7 +13801,7 @@ namespace cimg_library_suffixed { } } break; default : - if (error_message) std::sprintf(error_message, + 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; @@ -13493,7 +13812,7 @@ namespace cimg_library_suffixed { cimglist_for(colors,c) { const CImg& color = colors[c]; if (!color) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines no color for primitive [%u]", _width,primitives._width,c); return false; @@ -13504,7 +13823,7 @@ namespace cimg_library_suffixed { if (colors._width>primitives._width) { const CImg &light = colors.back(); if (!light || light._depth>1) { - if (error_message) std::sprintf(error_message, + 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); @@ -13530,7 +13849,7 @@ namespace cimg_library_suffixed { // 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; @@ -13538,7 +13857,7 @@ namespace cimg_library_suffixed { 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; } @@ -13550,7 +13869,7 @@ namespace cimg_library_suffixed { if (!full_check) { const unsigned long minimal_size = 8UL + 3*nb_points + 6*nb_primitives; if (_data + minimal_size>ptre) { - if (error_message) std::sprintf(error_message, + 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,size(),minimal_size); return false; @@ -13560,13 +13879,13 @@ namespace cimg_library_suffixed { // Check consistency of vertex data. if (!nb_points) { if (nb_primitives) { - if (error_message) std::sprintf(error_message, + 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, + 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":""); @@ -13575,7 +13894,7 @@ namespace cimg_library_suffixed { return true; } if (ptrs + 3*nb_points>ptre) { - if (error_message) std::sprintf(error_message, + 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; @@ -13584,7 +13903,7 @@ namespace cimg_library_suffixed { // Check consistency of primitive data. if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines %u vertices but no primitive", nb_points,nb_primitives,nb_points); return false; @@ -13598,7 +13917,7 @@ namespace cimg_library_suffixed { case 1 : { // Point. const unsigned int i0 = cimg::float2uint((float)*(ptrs++)); if (i0>=nb_points) { - if (error_message) std::sprintf(error_message, + 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; @@ -13610,7 +13929,7 @@ namespace cimg_library_suffixed { i1 = cimg::float2uint((float)*(ptrs++)); ptrs+=3; if (i0>=nb_points || i1>=nb_points) { - if (error_message) std::sprintf(error_message, + 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); @@ -13623,7 +13942,7 @@ namespace cimg_library_suffixed { 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, + 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); @@ -13637,7 +13956,7 @@ namespace cimg_library_suffixed { 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, + 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); @@ -13652,7 +13971,7 @@ namespace cimg_library_suffixed { 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, + 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); @@ -13660,13 +13979,13 @@ namespace cimg_library_suffixed { } } break; default : - if (error_message) std::sprintf(error_message, + 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, + 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)); @@ -13676,7 +13995,7 @@ namespace cimg_library_suffixed { // Check consistency of color data. if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines no color/texture data", nb_points,nb_primitives); return false; @@ -13690,7 +14009,7 @@ namespace cimg_library_suffixed { s = (unsigned int)*(ptrs - 1); if (!h && !s) { if (w>=c) { - if (error_message) std::sprintf(error_message, + 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); @@ -13699,7 +14018,7 @@ namespace cimg_library_suffixed { } else ptrs+=w*h*s; } if (ptrs>ptre) { - if (error_message) std::sprintf(error_message, + 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)); @@ -13709,7 +14028,7 @@ namespace cimg_library_suffixed { // Check consistency of opacity data. if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines no opacity data", nb_points,nb_primitives); return false; @@ -13722,7 +14041,7 @@ namespace cimg_library_suffixed { s = (unsigned int)*(ptrs - 1); if (!h && !s) { if (w>=o) { - if (error_message) std::sprintf(error_message, + 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); @@ -13731,7 +14050,7 @@ namespace cimg_library_suffixed { } else ptrs+=w*h*s; } if (ptrs>ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", nb_points,nb_primitives,o); return false; @@ -13740,7 +14059,7 @@ namespace cimg_library_suffixed { // Check end of data. if (ptrs1?"s":""); return false; @@ -13759,957 +14078,2511 @@ namespace cimg_library_suffixed { //@{ //------------------------------------- - // Define the math formula parser/compiler and evaluator. + // Define the math formula parser/compiler and expression evaluator. struct _cimg_math_parser { - CImgList code; - CImg opcode; - const CImg* p_code; - CImgList labelM; - CImg level, labelMpos, label1pos; CImg mem; - CImg expr; - const CImg& reference; - CImg reference_stats; - double median_value; - bool is_median_value; - unsigned int mempos, result; + CImgList _code, &code; + CImg opcode; + const CImg *p_code_begin, *p_code_end, *p_code; + + CImg expr, pexpr; + const CImg& imgin; + const CImgList& listin; + CImg &imgout; + CImgList& listout; + + CImg _img_stats, &img_stats; + CImgList _list_stats, &list_stats, _list_median, &list_median; + CImg mem_img_stats; + + CImg level, labelMpos, reserved_label; + CImgList labelM; + + unsigned int mempos, mem_img_median, debug_indent, init_size; + double *result; const char *const calling_function; typedef double (*mp_func)(_cimg_math_parser&); #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_constant(val) _cimg_mp_return(constant(val)) +#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)); } #define _cimg_mp_opcode6(op,i1,i2,i3,i4,i5,i6) \ { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5, _i6 = i6; \ _cimg_mp_return(opcode6(op,_i1,_i2,_i3,_i4,_i5,_i6)); } - -#if defined(_WIN64) - // On Win64 and gcc 4.7, sizeof(long)!=sizeof(pointer), so a workaround is needed.. -#define _cimg_mp_enfunc(op) (long)((char*)(op) - (char*)mp_u) -#define _cimg_mp_defunc(mp) (*(mp_func)((char*)mp_u + (mp).opcode[0]))(mp) -#else -#define _cimg_mp_enfunc(op) (long)(op) -#define _cimg_mp_defunc(mp) (*(mp_func)((mp).opcode[0]))(mp) -#endif +#define _cimg_mp_opcode7(op,i1,i2,i3,i4,i5,i6,i7) \ + { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5, _i6 = i6, _i7 = i7; \ + _cimg_mp_return(opcode7(op,_i1,_i2,_i3,_i4,_i5,_i6,_i7)); } +#define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) // Constructors. - _cimg_math_parser():reference(CImg::empty()),median_value(0),is_median_value(false),calling_function(0) {} - - _cimg_math_parser(const CImg& img, const char *const expression, const char *const funcname=0): - reference(img),median_value(0),is_median_value(false),calling_function(funcname?funcname:"cimg_math_parser") { - unsigned int l = 0; - if (expression) { - l = (unsigned int)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 = (unsigned int)(d - expr._data); - } - } - if (!l) throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Empty specified expression.", - pixel_type(),calling_function); + _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): + code(_code),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), + mem_img_median(~0U),debug_indent(0),init_size(0), + calling_function(funcname?funcname:"cimg_math_parser") { + if (!expression || !*expression) + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Empty specified expression.", + pixel_type(),calling_function); + const char *_expression = expression; + while (*_expression && *_expression<=' ') ++_expression; + CImg::string(_expression).move_to(expr); + level.assign(expr._width - 1); + + // Ease the retrieval of previous non-space characters afterwards. + pexpr.assign(expr._width); + char *pe = pexpr._data; + for (char *ps = expr._data, c = ' '; *ps; ++ps) { + if (*ps!=' ') c = *ps; + *(pe++) = c; + } + *pe = 0; int lv = 0; // Count parentheses/brackets 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=='('||*ps=='['?lv++:*ps==')'||*ps==']'?--lv:lv); if (lv!=0) { + cimg::strellipsize(expr,64); throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Unbalanced parentheses/brackets in specified expression '%s'.", + "CImg<%s>::%s(): Unbalanced parentheses/brackets in expression '%s'.", pixel_type(),calling_function, expr._data); } + // Init constant values. - mem.assign(512); - mem[0] = 0; - mem[1] = 1; - mem[2] = 2; - mem[3] = (double)reference._width; - mem[4] = (double)reference._height; - mem[5] = (double)reference._depth; - mem[6] = (double)reference._spectrum; - mem[7] = cimg::PI; - mem[8] = std::exp(1.0); // Then [9] = x, [10] = y, [11] = z, [12] = c - mempos = 13; + mem.assign(128,2); + double *p_mem = mem._data; + for (unsigned int i = 0; i<=10; ++i) *(p_mem++) = (double)i; // mem[0-10] + for (unsigned int i = 1; i<=5; ++i) *(p_mem++) = -(double)i; // mem[11-15] + *(p_mem++) = 0.5; // mem[16] + *(p_mem++) = 0; // mem[17] = thread_id + *(p_mem++) = (double)imgin._width; // mem[18] + *(p_mem++) = (double)imgin._height; // mem[19] + *(p_mem++) = (double)imgin._depth; // mem[20] + *(p_mem++) = (double)imgin._spectrum; // mem[21] + *(p_mem++) = (double)imgin._is_shared; // mem[22] + *(p_mem++) = (double)imgin._width*imgin._height; // mem[23] + *(p_mem++) = (double)imgin._width*imgin._height*imgin._depth; // mem[24] + *(p_mem++) = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // mem[25] + *(p_mem++) = cimg::PI; // mem[26] + *(p_mem++) = std::exp(1.0); // mem[27] + *(p_mem++) = 0; // mem[28] + *(p_mem++) = 0; // mem[29] + + // Then, [30] = x, [31] = y, [32] = z and [33] = c. +#define _cimg_mp_x 30 +#define _cimg_mp_y 31 +#define _cimg_mp_z 32 +#define _cimg_mp_c 33 + + // Set constant/variable property : { 1 = constant | -1 = variable | 0 = other }. + std::memset(mem.data(0,1),0,sizeof(double)*mem._width); + p_mem = mem.data(0,1); for (unsigned int i = 0; i<28; ++i) *(p_mem++) = 1; + mem(17,1) = 0; + + mempos = 34; labelMpos.assign(8); - label1pos.assign(128,1,1,1,~0U); - label1pos['w'] = 3; - label1pos['h'] = 4; - label1pos['d'] = 5; - label1pos['s'] = 6; - label1pos[0] = 7; // pi - label1pos['e'] = 8; - label1pos['x'] = 9; - label1pos['y'] = 10; - label1pos['z'] = 11; - label1pos['c'] = 12; - result = compile(expr._data,expr._data + l); // Compile formula into a serie of opcodes. + reserved_label.assign(128,1,1,1,~0U); + reserved_label['t'] = 17; + reserved_label['w'] = 18; + reserved_label['h'] = 19; + reserved_label['d'] = 20; + reserved_label['s'] = 21; + reserved_label['r'] = 22; + reserved_label[0] = 23; // wh + reserved_label[1] = 24; // whd + reserved_label[2] = 25; // whds + reserved_label[3] = 26; // pi + reserved_label['e'] = 27; + reserved_label[29] = 28; // interpolation + reserved_label[30] = 29; // boundary + reserved_label['x'] = _cimg_mp_x; + reserved_label['y'] = _cimg_mp_y; + reserved_label['z'] = _cimg_mp_z; + reserved_label['c'] = _cimg_mp_c; + // reserved_label[4-30] store also two-char variables: + // [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, + + // Compile expression into a serie of opcodes. + const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1); + p_code_end = code.end(); + + // Free resources used for parsing and prepare for evaluation. + mem.resize(mempos,1,1,1,-1); + result = mem._data + ind_result; + level.assign(); + labelMpos.assign(); + reserved_label.assign(); + expr.assign(); + pexpr.assign(); + opcode._width = opcode._depth = opcode._spectrum = 1; + opcode._is_shared = true; + + // Execute init() function if any specified. + p_code_begin = code._data + init_size; + if (init_size) { + mem[_cimg_mp_x] = mem[_cimg_mp_y] = mem[_cimg_mp_z] = mem[_cimg_mp_c] = 0; + for (p_code = code._data; p_code &op = *p_code; + // Allows to avoid parameter passing to evaluation functions. + opcode._data = op._data; opcode._height = op._height; + const uptrT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + } + + _cimg_math_parser(): + code(_code),p_code_begin(0),p_code_end(0), + 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), + calling_function(0) { + mem.assign(1 + _cimg_mp_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_begin(mp.p_code_begin),p_code_end(mp.p_code_end), + 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(mem._data + (mp.result - mp.mem._data)),calling_function(0) { +#ifdef cimg_use_openmp + mem[17] = omp_get_thread_num(); +#endif + opcode._width = opcode._depth = opcode._spectrum = 1; + opcode._is_shared = true; + } + + // Return 'true' is the specified mathematical expression requires the input image to be copied. + // Set 'is_parallelizable' to 'false' if expression should be evaluated with a single thread. + static bool needs_input_copy(const char *expression, bool &is_parallelizable) { + if (!expression || *expression=='>' || *expression=='<') return is_parallelizable = false; + for (const char *s = expression; *s; ++s) + if ((*s=='i' || *s=='j') && (s[1]=='(' || s[1]=='[')) { + if (s[2]=='#') is_parallelizable = false; + else { + const char opening = s[1], ending = opening=='('?')':']'; + const char *ns; + int level = 0; + for (ns = s + 2; *ns; ++ns) { // Find ending ')' or ']'. + if (*ns==ending && !level) break; + if (*ns==opening) ++level; else if (*ns==ending) --level; + } + if (*ns && (ns[1]!='=' || ns[2]=='=')) return true; + } + } else if (((*s=='R' || *s=='G' || *s=='B') && s[1]!='#') || + (*s=='i' && s[1]>='0' && s[1]<='7' && s[2]!='#')) return true; + return false; + } + + // Compilation procedure. + unsigned int compile(char *ss, char *se, unsigned int *p_coords=0) { + const char *const ss0 = ss; + if (ssss && *(se - 1)==' ') --se; + } + if (se>ss && *(se - 1)==';') --se; + if (se<=ss || !*ss) { + cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Missing item in expression '%s'.", + pixel_type(),calling_function, + expr._data); + } + unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6; + char + *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, + *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4, + *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8, + *s, *ps, *ns, *s0, *s1, *s2, *s3, c1, c2, c3, c4, sep, end; + + if (p_coords) + p_coords[0] = p_coords[1] = p_coords[2] = p_coords[3] = p_coords[4] = p_coords[5] = + p_coords[6] = p_coords[7] = p_coords[8] = p_coords[9] = p_coords[10] = ~0U; + + const char saved_char = *se; *se = 0; + const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; + bool is_sth; + CImg coords; + CImgList _opcode; + CImg variable_name; + + // Look for a single value, pre-defined variable or a variable assignment. + double val, val1, val2; + sep = end = 0; + int nb = cimg_sscanf(ss,"%lf%c%c",&val,&sep,&end); + +#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; + } +#endif + if (nb==1) _cimg_mp_constant(val); + if (nb==2 && sep=='%') _cimg_mp_constant(val/100); + + if (ss1==se) switch (*ss) { // One-char variable. + case 't' : case 'w' : case 'h' : case 'd' : case 's' : case 'r' : + case 'x' : case 'y' : case 'z' : case 'c' : case 'e' : + _cimg_mp_return(reserved_label[*ss]); + case 'u' : + if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']); + _cimg_mp_opcode2(mp_u,0,1); + case 'g' : + if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']); + _cimg_mp_opcode0(mp_g); + case 'i' : + if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']); + _cimg_mp_opcode0(mp_i); + case 'R' : + if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']); + _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,0,0,0); + case 'G' : + if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']); + _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,1,0,0); + case 'B' : + if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']); + _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,2,0,0); + case 'A' : + if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']); + _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,3,0,0); + } + else if (ss2==se) { // Two-chars variable. + arg1 = arg2 = ~0U; + if (*ss=='w' && *ss1=='h') _cimg_mp_return(reserved_label[0]); // wh + if (*ss=='p' && *ss1=='i') _cimg_mp_return(reserved_label[3]); // pi + if (*ss=='i') { + if (*ss1>='0' && *ss1<='9') { // i0...i9 + pos = 19 + *ss1 - '0'; + if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); + _cimg_mp_opcode6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_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; // is + case 'c' : // ic + if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); + if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; + _cimg_mp_return(mem_img_median); + break; + } + } + else if (*ss1=='m') switch (*ss) { + case 'x' : arg1 = 11; arg2 = 4; break; // xm + case 'y' : arg1 = 12; arg2 = 5; break; // ym + case 'z' : arg1 = 13; arg2 = 6; break; // zm + case 'c' : arg1 = 14; arg2 = 7; break; // cm + } + else if (*ss1=='M') switch (*ss) { + case 'x' : arg1 = 15; arg2 = 8; break; // xM + case 'y' : arg1 = 16; arg2 = 9; break; // yM + case 'z' : arg1 = 17; arg2 = 10; break; // zM + case 'c' : arg1 = 18; arg2 = 11; break; // cM + } + if (arg1!=~0U) { + if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); + if (!img_stats) { + img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false); + mem_img_stats.assign(1,14,1,1,~0U); + } + if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]); + _cimg_mp_return(mem_img_stats[arg2]); + } + } else if (ss3==se) { // Three-chars variable. + if (*ss=='w' && *ss1=='h' && *ss2=='d') _cimg_mp_return(reserved_label[1]); // whd + } else if (ss4==se) { // Four-chars variable. + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') _cimg_mp_return(reserved_label[2]); // whds + } + + for (s = se2; s>ss; --s) // Separator ';'. + if (*s==';' && level[s - expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s + 1,se,p_coords)); } + + // Variable declaration/assignment or pixel assignment. + for (s = ss1, ps = ss, ns = ss2; s2 && (*ss=='i' || *ss=='j')) { + char *const ve1 = ss + l_variable_name - 1; + is_sth = *ss=='j'; // is_relative? + if (*ss1=='(' && *ve1==')') { // i/j(_#ind,_x,_y,_z,_c) = value + if (*ss2=='#') { + s0 = ss3; while (s0='0' && *variable_name<='9') is_sth = false; + else for (ns = variable_name._data; *ns; ++ns) + if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') { + is_sth = false; break; + } + if (!is_sth) { // Not a valid name. + is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? + if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment. + + // Pixel assignment (generic lvalue). + if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { + coords.assign(11); + arg1 = compile(ss,s,coords); + arg2 = compile(s + 1,se); + if (*coords!=~0U || coords[1]!=~0U || coords[5]!=~0U || coords[6]!=~0U) { + p1 = coords[10]; + if (*coords!=~0U) {// i[_#ind,_off] = value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_ioff,arg1,p1,*coords,arg2).move_to(code); + else CImg::vector((uptrT)mp_set_ioff,arg1,*coords,arg2).move_to(code); + } else if (coords[1]!=~0U) { // i(_#ind,_x,_y,_z,_c) = value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_ixyzc,arg1, + p1,coords[1],coords[2],coords[3],coords[4],arg2).move_to(code); + else CImg::vector((uptrT)mp_set_ixyzc,arg1, + coords[1],coords[2],coords[3],coords[4],arg2).move_to(code); + } else if (coords[5]!=~0U) { // j[_#ind,_off] = value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_joff,arg1,p1,coords[5],arg2).move_to(code); + else CImg::vector((uptrT)mp_set_joff,arg1,coords[5],arg2).move_to(code); + } else if (coords[6]!=~0U) { // j(_#ind,x,y,z,c) = value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_jxyzc,arg1, + p1,coords[6],coords[7],coords[8],coords[9],arg2).move_to(code); + else CImg::vector((uptrT)mp_set_jxyzc,arg1, + coords[6],coords[7],coords[8],coords[9],arg2).move_to(code); + } + if (p_coords) std::memcpy(p_coords,coords,coords._width*sizeof(unsigned int)); + _cimg_mp_return(arg1); + } + } + *se = saved_char; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Invalid variable name '%s' in expression " + "'%s%s%s'.", + pixel_type(),calling_function, + variable_name._data, + (ss - 8)>expr._data?"...":"", + (ss - 8)>expr._data?ss - 8:expr._data, + se<&expr.back()?"...":""); + } + + // Check for particular case of a reserved variable assignment. + if (variable_name[1] && !variable_name[2]) { // Two-chars variable. + c1 = variable_name[0]; + c2 = variable_name[1]; + if (c1=='w' && c2=='h') variable_name.fill((char)0,(char)0); // wh + else if (c1=='p' && c2=='i') variable_name.fill(3,0); // pi + else if (c1=='i') { + if (c2>='0' && c2<='9') variable_name.fill(19 + c2 - '0',0); // i0...i9 + else if (c2=='m') variable_name.fill(4,0); // im + else if (c2=='M') variable_name.fill(5,0); // iM + else if (c2=='a') variable_name.fill(6,0); // ia + else if (c2=='v') variable_name.fill(7,0); // iv + else if (c2=='s') variable_name.fill(8,0); // is + else if (c2=='p') variable_name.fill(9,0); // ip + else if (c2=='c') variable_name.fill(10,0); // ic + } else if (c2=='m') { + if (c1=='x') variable_name.fill(11,0); // xm + else if (c1=='y') variable_name.fill(12,0); // ym + else if (c1=='z') variable_name.fill(13,0); // zm + else if (c1=='c') variable_name.fill(14,0); // cm + } else if (c2=='M') { + if (c1=='x') variable_name.fill(15,0); // xM + else if (c1=='y') variable_name.fill(16,0); // yM + else if (c1=='z') variable_name.fill(17,0); // zM + else if (c1=='c') variable_name.fill(18,0); // 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') variable_name.fill(1,0); // 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') variable_name.fill(2,0); // whds + } else if (!std::strcmp(variable_name,"interpolation")) variable_name.fill(29,0); + else if (!std::strcmp(variable_name,"boundary")) variable_name.fill(30,0); + + // Set new value to variable. + arg2 = compile(s + 1,se); + if (!variable_name[1] && *variable_name>=0) { // One-char variable, or variable in reserved_labels. + arg1 = reserved_label[*variable_name]; + if (arg1==~0U) // New variable. + arg1 = reserved_label[*variable_name] = opcode1(mp_replace,arg2); + else // Already declared (or reserved). + CImg::vector((uptrT)mp_replace,arg1,arg2).move_to(code); + } else { + int label_pos = -1; + cimglist_for(labelM,i) // Check for existing variable with same name. + if (!std::strcmp(variable_name,labelM[i])) { label_pos = i; break; } + if (label_pos<0) { // New variable. + if (labelM._width>=labelMpos._width) labelMpos.resize(-200,1,1,1,0); + label_pos = labelM.width(); + variable_name.move_to(labelM); + arg1 = labelMpos[label_pos] = opcode1(mp_replace,arg2); + } else { // Already declared. + arg1 = labelMpos[label_pos]; + CImg::vector((uptrT)mp_replace,arg1,arg2).move_to(code); + } + } + mem(arg1,1) = -1; // Set variable property. + _cimg_mp_return(arg1); + } + + // Look for 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=='*' || *ps=='/' || *ps=='%' || + *ps=='&' || *ps=='^' || *ps=='|' || + (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && + level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=). + const char *s_op; + mp_func op; + switch (*ps) { + case '+' : op = mp_self_add; s_op = "addition"; break; + case '-' : op = mp_self_sub; s_op = "subtraction"; break; + case '*' : op = mp_self_mul; s_op = "multiplication"; break; + case '/' : op = mp_self_div; s_op = "division"; break; + case '%' : op = mp_self_modulo; s_op = "modulo"; break; + case '<' : op = mp_self_bitwise_left_shift; s_op = "left bitshift"; break; + case '>' : op = mp_self_bitwise_right_shift; s_op = "right bitshift"; break; + case '&' : op = mp_self_bitwise_and; s_op = "bitwise and"; break; + case '|' : op = mp_self_bitwise_or; s_op = "bitwise or"; break; + default : op = mp_self_power; s_op = "power"; break; + } + + coords.assign(11); + arg1 = compile(ss,*ps=='>' || *ps=='<'?ns:ps,coords); + arg2 = compile(s + 1,se); + CImg::vector((uptrT)op,arg1,arg2).move_to(code); + if (*coords!=~0U || coords[1]!=~0U || coords[5]!=~0U || coords[6]!=~0U) { // Modify pixel value. + p1 = coords[10]; + if (*coords!=~0U) { // i[_#ind,off] + = value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_ioff,arg1,p1,*coords,arg1).move_to(code); + else CImg::vector((uptrT)mp_set_ioff,arg1,*coords,arg1).move_to(code); + } else if (coords[1]!=~0U) { // i(_#ind,_x,_y,_z,_c) += value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_ixyzc,arg1, + p1,coords[1],coords[2],coords[3],coords[4],arg1).move_to(code); + else CImg::vector((uptrT)mp_set_ixyzc,arg1, + coords[1],coords[2],coords[3],coords[4],arg1).move_to(code); + } else if (coords[5]!=~0U) { // j[_#ind,off] += value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_joff,arg1,p1,coords[5],arg1).move_to(code); + else CImg::vector((uptrT)mp_set_joff,arg1,coords[5],arg1).move_to(code); + } else if (coords[6]!=~0U) { // j(_#ind,_x,_y,_z,_c) += value + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_jxyzc,arg1, + p1,coords[6],coords[7],coords[8],coords[9],arg1).move_to(code); + else CImg::vector((uptrT)mp_set_jxyzc,arg1, + coords[6],coords[7],coords[8],coords[9],arg1).move_to(code); + } + if (p_coords) std::memcpy(p_coords,coords,coords._width*sizeof(unsigned int)); + } else if (mem(arg1,1)>=0) { + *se = saved_char; + variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0; + cimg::strellipsize(variable_name,64); + cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Invalid self-%s of non-variable '%s' " + "in expression '%s%s%s'.", + pixel_type(),calling_function, + s_op,variable_name._data, + (ss - 8)>expr._data?"...":"", + (ss - 8)>expr._data?ss - 8:expr._data, + se<&expr.back()?"...":""); + } + _cimg_mp_return(arg1); + } + + for (s = ss1; s0 && mem(arg2,1)>0 && mem(arg3,1)>0) _cimg_mp_constant(mem[arg1]?mem[arg2]:mem[arg3]); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + pos = mempos++; + CImg::vector((uptrT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3).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. + arg1 = compile(ss,s); + p2 = code._width; arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] || mem[arg2]); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + pos = mempos++; + CImg::vector((uptrT)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. + arg1 = compile(ss,s); + p2 = code._width; arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] && mem[arg2]); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + pos = mempos++; + CImg::vector((uptrT)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. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((unsigned long)mem[arg1] | (unsigned long)mem[arg2]); + _cimg_mp_opcode2(mp_bitwise_or,arg1,arg2); + } + + for (s = se2; s>ss; --s) + if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((unsigned long)mem[arg1] & (unsigned long)mem[arg2]); + _cimg_mp_opcode2(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. + arg1 = compile(ss,s); arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]!=mem[arg2]); + _cimg_mp_opcode2(mp_neq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to. + arg1 = compile(ss,s); arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]==mem[arg2]); + _cimg_mp_opcode2(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. + arg1 = compile(ss,s); arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]<=mem[arg2]); + _cimg_mp_opcode2(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. + arg1 = compile(ss,s); arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]>=mem[arg2]); + _cimg_mp_opcode2(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. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]ss; --s, --ns, --ps) + if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]>mem[arg2]); + _cimg_mp_opcode2(mp_gt,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift. + arg1 = compile(ss,s); arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((long)mem[arg1]<<(unsigned int)mem[arg2]); + _cimg_mp_opcode2(mp_bitwise_left_shift,compile(ss,s),compile(s + 2,se)); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift. + arg1 = compile(ss,s); arg2 = compile(s + 2,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant((long)mem[arg1]>>(unsigned int)mem[arg2]); + _cimg_mp_opcode2(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. + arg1 = compile(ss,s); + arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] + mem[arg2]); + if (arg2==1) _cimg_mp_opcode1(mp_increment,arg1); + if (arg1==1) _cimg_mp_opcode1(mp_increment,arg2); + _cimg_mp_opcode2(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. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1] - mem[arg2]); + if (arg2==1) _cimg_mp_opcode1(mp_decrement,arg1); + _cimg_mp_opcode2(mp_sub,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]*mem[arg2]); + _cimg_mp_opcode2(mp_mul,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(mem[arg1]/mem[arg2]); + _cimg_mp_opcode2(mp_div,arg1,arg2); + } + + for (s = se2, ns = se1; s>ss; --s, --ns) if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo. + arg1 = compile(ss,s); arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); + _cimg_mp_opcode2(mp_modulo,arg1,arg2); + } + + if (se1>ss) { + if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) // Unary plus. + _cimg_mp_return(compile(ss1,se)); + + if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus. + arg1 = compile(ss1,se); + if (mem(arg1,1)>0) _cimg_mp_constant(-mem[arg1]); + _cimg_mp_opcode1(mp_minus,arg1); + } + + if (*ss=='!') { // Logical not. + arg1 = compile(ss1,se); + if (mem(arg1,1)>0) _cimg_mp_constant(!mem[arg1]); + _cimg_mp_opcode1(mp_logical_not,arg1); + } + + if (*ss=='~') { // Bitwise not. + arg1 = compile(ss1,se); + if (mem(arg1,1)>0) _cimg_mp_constant(~(unsigned long)mem[arg1]); + _cimg_mp_opcode1(mp_bitwise_not,arg1); + } + } + + for (s = se2; s>ss; --s) + if (*s=='^' && level[s - expr._data]==clevel) { // Power. + arg1 = compile(ss,s); + arg2 = compile(s + 1,se); + if (mem(arg1,1)>0 && mem(arg2,1)>0) _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); + switch (arg2) { + case 0 : _cimg_mp_return(1); + case 1 : _cimg_mp_return(arg1); + case 2 : _cimg_mp_opcode1(mp_sqr,arg1); + case 3 : _cimg_mp_opcode1(mp_pow3,arg1); + case 4 : _cimg_mp_opcode1(mp_pow4,arg1); + default : _cimg_mp_opcode2(mp_pow,compile(ss,s),compile(s + 1,se)); + } + } + + is_sth = ss1ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment. + coords.assign(11); + arg1 = is_sth?compile(ss2,se,coords):compile(ss,se2,coords); + pos = is_sth?arg1:opcode1(mp_replace,arg1); + CImg::vector((uptrT)((is_sth && *ss=='+') || (!is_sth && *se1=='+')?mp_self_increment: + mp_self_decrement),arg1).move_to(code); + if (*coords!=~0U || coords[1]!=~0U || coords[5]!=~0U || coords[6]!=~0U) { // Assign pixel. + p1 = coords[10]; + if (*coords!=~0U) { // i[_#ind,off]++ + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_ioff,arg1,p1,*coords,arg1).move_to(code); + else CImg::vector((uptrT)mp_set_ioff,arg1,*coords,arg1).move_to(code); + } else if (coords[1]!=~0U) { // i(_#ind,_x,_y,_z,_c)++ + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_ixyzc,arg1, + p1,coords[1],coords[2],coords[3],coords[4],arg1).move_to(code); + else CImg::vector((uptrT)mp_set_ixyzc,arg1, + coords[1],coords[2],coords[3],coords[4],arg1).move_to(code); + } else if (coords[5]!=~0U) { // j[#_ind,off]++ + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_joff,arg1,p1,coords[5],arg1).move_to(code); + else CImg::vector((uptrT)mp_set_joff,arg1,coords[5],arg1).move_to(code); + } else if (coords[6]!=~0U) { // j(#_ind,_x,_y,_z,_c)++ + if (p1!=~0U) CImg::vector((uptrT)mp_list_set_jxyzc,arg1, + p1,coords[6],coords[7],coords[8],coords[9],arg1).move_to(code); + else CImg::vector((uptrT)mp_set_jxyzc,arg1, + coords[6],coords[7],coords[8],coords[9],arg1).move_to(code); + } + if (p_coords && is_sth) std::memcpy(p_coords,coords,coords._width*sizeof(unsigned int)); + } else if (mem(arg1,1)>=0) { + *se = saved_char; + 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::strellipsize(variable_name,64); + cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Invalid %s-%s of non-variable '%s' in expression '%s%s%s'.", + pixel_type(),calling_function, + is_sth?"pre":"post", + is_sth?(*ss=='+'?"increment":"decrement"): + *se1=='+'?"increment":"decrement", + variable_name._data, + (ss - 8)>expr._data?"...":"", + (ss - 8)>expr._data?ss - 8:expr._data, + se<&expr.back()?"...":""); + } + _cimg_mp_return(pos); + } + + // Array-like access to image values 'i[_#ind,offset,_boundary]' and 'j[_#ind,offset,_boundary]'. + if (*se1==']') { + is_sth = *ss=='j'; // is_relative? + if ((*ss=='i' || is_sth) && *ss1=='[') { + if (*ss2=='#') { + s0 = ss3; while (s00) _cimg_mp_constant(cimg::abs(mem[arg1])); + _cimg_mp_opcode1(mp_abs,arg1); + } + + if (!std::strncmp(ss,"asin(",5)) { // Arcsin. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::asin(mem[arg1])); + _cimg_mp_opcode1(mp_asin,arg1); + } + + if (!std::strncmp(ss,"acos(",5)) { // Arccos. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::acos(mem[arg1])); + _cimg_mp_opcode1(mp_acos,arg1); + } + + if (!std::strncmp(ss,"atan(",5)) { // Arctan. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::atan(mem[arg1])); + _cimg_mp_opcode1(mp_atan,arg1); + } + + if (!std::strncmp(ss,"atan2(",6)) { // Arctan2. + s1 = ss6; while (s10 && mem(arg2,1)>0) _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2])); + _cimg_mp_opcode2(mp_atan2,arg1,arg2); + } + break; + + case 'c' : + if (!std::strncmp(ss,"cos(",4)) { // Cosine. + arg1 = compile(ss4,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::cos(mem[arg1])); + _cimg_mp_opcode1(mp_cos,arg1); + } + + if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::cosh(mem[arg1])); + _cimg_mp_opcode1(mp_cosh,arg1); + } + + if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::pow(mem[arg1],1.0/3)); + _cimg_mp_opcode1(mp_cbrt,arg1); + } + + if (!std::strncmp(ss,"cut(",4)) { // Cut. + s1 = ss4; while (s10 && mem(arg2,1)>0 && mem(arg3,1)>0) { + val = mem[arg1]; val1 = mem[arg2]; val2 = mem[arg3]; + _cimg_mp_constant(valval2?val2:val); + } + _cimg_mp_opcode3(mp_cut,arg1,arg2,arg3); + } + break; + + case 'd' : + if (!std::strncmp(ss,"dowhile",7) && (*ss7=='(' || (*ss7==' ' && *ss8=='('))) { // Do..while + if (*ss7==' ') cimg::swap(*ss7,*ss8); // Allow space before opening brace. + s1 = ss8; while (s1::vector((uptrT)mp_dowhile,arg1,arg2,code._width - p1). + move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"date(",5)) { // Date and file date. + s1 = ss5; while (s1::vector((uptrT)mp_debug,arg1,code._width - p1), + CImg::string(ss6).unroll('y'))>'y').move_to(code,p1); + *se1 = ')'; + _cimg_mp_return(arg1); + } + break; + + case 'e' : + if (!std::strncmp(ss,"exp(",4)) { // Exponential. + arg1 = compile(ss4,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::exp(mem[arg1])); + _cimg_mp_opcode1(mp_exp,arg1); + } + break; + + case 'f' : + if (*ss1=='o' && *ss2=='r' && (*ss3=='(' || (*ss3==' ' && *ss4=='('))) { // For.. + if (*ss3==' ') cimg::swap(*ss3,*ss4); // Allow space before opening brace. + s1 = ss4; while (s1::vector((uptrT)mp_whiledo,pos,arg2,p3 - p2,code._width - p3). + move_to(code,p2); + _cimg_mp_return(pos); + } + break; + + case 'g' : + if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function. + s1 = ss6; while (s10 && mem(arg2,1)>0) { + val1 = mem[arg1]; val2 = mem[arg2]; + _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/std::sqrt(2*val2*val2*cimg::PI)); + } + _cimg_mp_opcode2(mp_gauss,arg1,arg2); + } + break; + + case 'h' : + if (!std::strncmp(ss,"hypot(",6)) { // Hypothenuse. + s1 = ss6; while (s10 && mem(arg2,1)>0) { + val1 = cimg::abs(mem[arg1]); val2 = cimg::abs(mem[arg2]); + if (val10) { val/=val1; _cimg_mp_constant(val1*std::sqrt(1+val*val)); } + _cimg_mp_constant(0); + } + _cimg_mp_opcode2(mp_hypot,arg1,arg2); + } + break; + + case 'i' : + if (!std::strncmp(ss,"int(",4)) { // Integer cast. + arg1 = compile(ss4,se1); + if (mem(arg1,1)>0) _cimg_mp_constant((long)mem[arg1]); + _cimg_mp_opcode1(mp_int,arg1); + } + + if (*ss1=='f' && (*ss2=='(' || (*ss2==' ' && *ss3=='('))) { // If..then[..else.] + if (*ss2==' ') cimg::swap(*ss2,*ss3); // Allow space before opening brace. + s1 = ss3; while (s1=se1?0:compile(s2 + 1,se1); + if (mem(arg1,1)>0 && mem(arg2,1)>0 && mem(arg3,1)>0) _cimg_mp_constant(mem[arg1]?mem[arg2]:mem[arg3]); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + pos = mempos++; + CImg::vector((uptrT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3).move_to(code,p2); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"init(",5)) { // Init. + if (ss0!=expr._data || code.width()) // (only allowed as the first instruction). + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Call to init() not done at the beginning " + "of expression '%s%s%s'.", + pixel_type(),calling_function, + (ss - 8)>expr._data?"...":"", + (ss - 8)>expr._data?ss - 8:expr._data, + se<&expr.back()?"...":""); + arg1 = compile(ss5,se1,p_coords); + init_size = code.width(); + _cimg_mp_return(arg1); + } + + if (*ss1=='s') { // Family of 'is_?()' functions. + + if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? + if (ss7==se1) _cimg_mp_return(0); + _cimg_mp_opcode1(mp_isbool,compile(ss7,se1)); + } + + if (!std::strncmp(ss,"isdir(",6)) { // Is directory? + *se1 = 0; + is_sth = cimg::is_directory(ss6); + *se1 = ')'; + _cimg_mp_return(is_sth?1U:0U); + } + + if (!std::strncmp(ss,"isfile(",7)) { // Is file? + *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? + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + pos = mempos++; + CImg::vector((uptrT)mp_isin,pos).move_to(_opcode); + for (s = ss5; s::vector(compile(s,ns)).move_to(_opcode); + s = ns; + } + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? + if (ss6==se1) _cimg_mp_return(0); + _cimg_mp_opcode1(mp_isinf,compile(ss6,se1)); + } + + if (!std::strncmp(ss,"isint(",6)) { // Is integer? + if (ss6==se1) _cimg_mp_return(0); + _cimg_mp_opcode1(mp_isint,compile(ss6,se1)); + } + + if (!std::strncmp(ss,"isnan(",6)) { // Is Nan? + if (ss6==se1) _cimg_mp_return(0); + _cimg_mp_opcode1(mp_isnan,compile(ss6,se1)); + } + + if (!std::strncmp(ss,"isval(",6)) { // Is value? + 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. + arg1 = compile(ss4,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::log(mem[arg1])); + _cimg_mp_opcode1(mp_log,arg1); + } + + if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(cimg::log2(mem[arg1])); + _cimg_mp_opcode1(mp_log2,arg1); + } + + if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm. + arg1 = compile(ss6,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::log10(mem[arg1])); + _cimg_mp_opcode1(mp_log10,arg1); + } + break; + + case 'n' : + if (!std::strncmp(ss,"narg(",5)) { // Number of arguments. + if (*ss5==')') _cimg_mp_return(0); + arg1 = 0; + for (s = ss5; s=mem._width) mem.resize(-200,2,1,1,0); + pos = mempos++; + switch (arg1) { + case 0 : CImg::vector((uptrT)mp_norm0,pos).move_to(_opcode); break; + case 1 : CImg::vector((uptrT)mp_norm1,pos).move_to(_opcode); break; + case 2 : CImg::vector((uptrT)mp_norm2,pos).move_to(_opcode); break; + case ~0U : CImg::vector((uptrT)mp_norminf,pos).move_to(_opcode); break; + default : + CImg::vector((uptrT)mp_normp,pos,(uptrT)(arg1==~0U?-1:(int)arg1)). + move_to(_opcode); + } + for (s = std::strchr(ss5,'(') + 1; s::vector(compile(s,ns)).move_to(_opcode); + s = ns; + } + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + break; + + case 'p' : + if (!std::strncmp(ss,"print(",6)) { // Print expression. + pos = compile(ss6,se1,p_coords); + *se1 = 0; + ((CImg::vector((uptrT)mp_print,pos), + CImg::string(ss6).unroll('y'))>'y').move_to(code); + *se1 = ')'; + _cimg_mp_return(pos); + } + break; + + case 'r' : + if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation. + s1 = ss4; while (s10 && mem(arg2,1)>0) + _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]): + cimg::ror(mem[arg1],(unsigned int)mem[arg2])); + _cimg_mp_opcode2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2); + } + + if (!std::strncmp(ss,"round(",6)) { // Value rounding. + s1 = ss6; while (s10 && mem(arg2,1)>0 && mem(arg3,1)>0) + _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3])); + _cimg_mp_opcode3(mp_round,arg1,arg2,arg3); + } + break; + + case 's' : + if (!std::strncmp(ss,"sin(",4)) { // Sine. + arg1 = compile(ss4,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::sin(mem[arg1])); + _cimg_mp_opcode1(mp_sin,arg1); + } + + if (!std::strncmp(ss,"sqr(",4)) { // Square. + arg1 = compile(ss4,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(cimg::sqr(mem[arg1])); + _cimg_mp_opcode1(mp_sqr,arg1); + } + + if (!std::strncmp(ss,"sign(",5)) { // Sign. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(cimg::sign(mem[arg1])); + _cimg_mp_opcode1(mp_sign,arg1); + } + + if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(cimg::sinc(mem[arg1])); + _cimg_mp_opcode1(mp_sinc,arg1); + } + + if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::sinh(mem[arg1])); + _cimg_mp_opcode1(mp_sinh,arg1); + } + + if (!std::strncmp(ss,"sqrt(",5)) { // Square root. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::sqrt(mem[arg1])); + _cimg_mp_opcode1(mp_sqrt,arg1); + } + break; + + case 't' : + if (!std::strncmp(ss,"tan(",4)) { // Tangent. + arg1 = compile(ss4,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::tan(mem[arg1])); + _cimg_mp_opcode1(mp_tan,arg1); + } + + if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent. + arg1 = compile(ss5,se1); + if (mem(arg1,1)>0) _cimg_mp_constant(std::tanh(mem[arg1])); + _cimg_mp_opcode1(mp_tanh,arg1); + } + break; + + case 'u' : + if (*ss1=='(') { // Random value with uniform distribution. + if (*ss2==')') _cimg_mp_opcode2(mp_u,0,1); + s1 = ss2; while (s1::vector((uptrT)mp_whiledo,arg2,arg1,p2 - p1,code._width - p2). + move_to(code,p1); + _cimg_mp_return(arg2); + } + break; + } + + if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) || + !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) || + !std::strncmp(ss,"arg(",4) || + !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7)) { // Multi-argument functions. + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + pos = mempos++; + is_sth = *ss=='a' && ss[3]!='('; + CImg::vector((uptrT)(*ss=='a'?(ss[3]=='('?mp_arg:ss[4]=='i'?mp_argmin:mp_argmax): + *ss=='k'?mp_kth:ss[1]=='i'?mp_min: + ss[1]=='a'?mp_max:mp_med),pos). + move_to(_opcode); + for (s = is_sth?ss7:ss4; s::vector(compile(s,ns)).move_to(_opcode); + s = ns; + } + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + + } // if (se1==')'). + + // Variables related to the input list of images. + if (*ss1=='#' && ss20?cimg::mod((int)mem[arg1],listin.width()):0); + switch (*ss) { + case 'w' : // w#ind + if (!listin) _cimg_mp_return(0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width); + _cimg_mp_opcode1(mp_list_width,arg1); + case 'h' : // h#ind + if (!listin) _cimg_mp_return(0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._height); + _cimg_mp_opcode1(mp_list_height,arg1); + case 'd' : // d#ind + if (!listin) _cimg_mp_return(0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._depth); + _cimg_mp_opcode1(mp_list_depth,arg1); + case 'r' : // r#ind + if (!listin) _cimg_mp_return(0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._is_shared); + _cimg_mp_opcode1(mp_list_is_shared,arg1); + case 's' : // s#ind + if (!listin) _cimg_mp_return(0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._spectrum); + _cimg_mp_opcode1(mp_list_spectrum,arg1); + case 'i' : // i#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,_cimg_mp_c, + reserved_label[29],reserved_label[30]); + case 'R' : // R#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,0, + reserved_label[29],reserved_label[30]); + case 'G' : // G#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,1, + reserved_label[29],reserved_label[30]); + case 'B' : // B#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,2, + reserved_label[29],reserved_label[30]); + case 'A' : // A#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,3, + reserved_label[29],reserved_label[30]); + } + } + + if (*ss1 && *ss2=='#' && ss30?cimg::mod((int)mem[arg1],listin.width()):0); + if (*ss=='w' && *ss1=='h') { // wh#ind + if (!listin) _cimg_mp_return(0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width*listin[p1]._height); + _cimg_mp_opcode1(mp_list_wh,arg1); + } + arg2 = ~0U; + + if (*ss=='i') { + if (*ss1=='c') { // ic#ind + if (!listin) _cimg_mp_return(0); + if (mem(arg1,1)>0) { + if (!list_median) list_median.assign(listin._width); + if (!list_median[p1]) CImg::vector(listin[p1].median()).move_to(list_median[p1]); + _cimg_mp_constant(*list_median[p1]); + } + _cimg_mp_opcode1(mp_list_median,arg1); + } + if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_opcode7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,*ss1 - '0', + reserved_label[29],reserved_label[30]); + } + 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 (mem(arg1,1)>0) { + 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_opcode2(mp_list_stats,arg1,arg2); + } + } + + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss40?cimg::mod((int)mem[arg1],listin.width()):0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth); + _cimg_mp_opcode1(mp_list_whd,arg1); + } + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss50?cimg::mod((int)mem[arg1],listin.width()):0); + if (mem(arg1,1)>0) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth* + listin[p1]._spectrum); + _cimg_mp_opcode1(mp_list_whds,arg1); + } + + if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(reserved_label[29]); // interpolation + if (!std::strcmp(ss,"boundary")) _cimg_mp_return(reserved_label[30]); // boundary + + // No known item found, assuming this is an already initialized variable. + variable_name.assign(ss,(unsigned int)(se - ss + 1)).back() = 0; + if (variable_name[1]) { // Multi-char variable. + cimglist_for(labelM,i) if (!std::strcmp(variable_name,labelM[i])) _cimg_mp_return(labelMpos[i]); + } else if (reserved_label[*variable_name]!=~0U) // Single-char variable. + _cimg_mp_return(reserved_label[*variable_name]); + + // Reached an unknown item -> error. + is_sth = true; // is_valid_variable_name. + if (*variable_name>='0' && *variable_name<='9') is_sth = false; + else for (ns = variable_name._data + 1; *ns; ++ns) + if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') { + is_sth = false; break; + } + + *se = saved_char; + cimg::strellipsize(variable_name,64); + cimg::strellipsize(expr,64); + if (is_sth) throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Undefined variable '%s' in expression '%s%s%s'.", + pixel_type(),calling_function, + variable_name._data, + (ss - 8)>expr._data?"...":"", + (ss - 8)>expr._data?ss - 8:expr._data, + se<&expr.back()?"...":""); + else throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): Invalid item '%s' in expression '%s%s%s'.", + pixel_type(),calling_function, + variable_name._data, + (ss - 8)>expr._data?"...":"", + (ss - 8)>expr._data?ss - 8:expr._data, + se<&expr.back()?"...":""); + } + + // Evaluation procedure. + double operator()(const double x, const double y, const double z, const double c) { + mem[_cimg_mp_x] = x; mem[_cimg_mp_y] = y; mem[_cimg_mp_z] = z; mem[_cimg_mp_c] = c; + for (p_code = p_code_begin; p_code &op = *p_code; + // Allows to avoid parameter passing to evaluation functions. + opcode._data = op._data; opcode._height = op._height; + const uptrT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + return *result; + } + + // Insert constant value in memory. + unsigned int constant(const double val) { + if (val==(double)(int)val) { + if (val>=0 && val<=9) return (unsigned int)val; + if (val<0 && val>=-5) return (unsigned int)(10 - val); + } + if (val==0.5) return 16; + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + const unsigned int pos = mempos++; + mem[pos] = val; mem(pos,1) = 1; // Set constant property. + return pos; } // Insert code instructions. unsigned int opcode0(const mp_func op) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos).move_to(code); + CImg::vector((uptrT)op,pos).move_to(code); return pos; } unsigned int opcode1(const mp_func op, const unsigned int arg1) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1).move_to(code); + CImg::vector((uptrT)op,pos,arg1).move_to(code); return pos; } unsigned int opcode2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1,arg2).move_to(code); + CImg::vector((uptrT)op,pos,arg1,arg2).move_to(code); return pos; } unsigned int opcode3(const mp_func op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1,arg2,arg3).move_to(code); + CImg::vector((uptrT)op,pos,arg1,arg2,arg3).move_to(code); + return pos; + } + + unsigned int opcode5(const mp_func 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,2,1,1,0); + const unsigned int pos = mempos++; + CImg::vector((uptrT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code); return pos; } unsigned int opcode6(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) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); + CImg::vector((uptrT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); return pos; } - // Compilation procedure. - unsigned int compile(char *const ss, char *const se) { - if (!ss || se<=ss || !*ss) { - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Missing item in specified expression '%s'.", - pixel_type(),calling_function, - expr._data); - } - 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 char saved_char = *se; *se = 0; - const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; - if (*se1==';') return compile(ss,se1); - - // 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 || val==1 || val==2) _cimg_mp_return((unsigned int)val); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = val; - _cimg_mp_return(pos); - } - if (nb==2 && sep=='%') { - if (val==0 || val==100 || val==200) _cimg_mp_return((unsigned int)(val/100)); - 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' : case 'h' : case 'd' : case 's' : - case 'x' : case 'y' : case 'z' : case 'c' : case 'e' : _cimg_mp_return(label1pos[*ss]); - case 'u' : if (label1pos['u']!=~0U) _cimg_mp_return(label1pos['u']); _cimg_mp_opcode2(mp_u,0,1); - case 'g' : if (label1pos['g']!=~0U) _cimg_mp_return(label1pos['g']); _cimg_mp_opcode0(mp_g); - case 'i' : if (label1pos['i']!=~0U) _cimg_mp_return(label1pos['i']); _cimg_mp_opcode0(mp_i); - case '?' : _cimg_mp_opcode2(mp_u,0,1); - } - if (ss1==se1) { - if (*ss=='p' && *ss1=='i') _cimg_mp_return(label1pos[0]); // pi - if (*ss=='i') { // im - if (*ss1=='m') { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[1]!=~0U) _cimg_mp_return(label1pos[1]); _cimg_mp_opcode0(mp_im); - } - if (*ss1=='M') { // iM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[2]!=~0U) _cimg_mp_return(label1pos[2]); _cimg_mp_opcode0(mp_iM); - } - if (*ss1=='a') { // ia - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[3]!=~0U) _cimg_mp_return(label1pos[3]); _cimg_mp_opcode0(mp_ia); - } - if (*ss1=='v') { // iv - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[4]!=~0U) _cimg_mp_return(label1pos[4]); _cimg_mp_opcode0(mp_iv); - } - if (*ss1=='c') { // ic - if (!is_median_value && reference) { median_value = reference.median(); is_median_value = true; } - if (label1pos[5]!=~0U) _cimg_mp_return(label1pos[5]); _cimg_mp_opcode0(mp_ic); - } - } - if (*ss1=='m') { - if (*ss=='x') { // xm - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[6]!=~0U) _cimg_mp_return(label1pos[6]); _cimg_mp_opcode0(mp_xm); - } - if (*ss=='y') { // ym - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[7]!=~0U) _cimg_mp_return(label1pos[7]); _cimg_mp_opcode0(mp_ym); - } - if (*ss=='z') { // zm - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[8]!=~0U) _cimg_mp_return(label1pos[8]); _cimg_mp_opcode0(mp_zm); - } - if (*ss=='c') { // cm - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[9]!=~0U) _cimg_mp_return(label1pos[9]); _cimg_mp_opcode0(mp_cm); - } - } - if (*ss1=='M') { - if (*ss=='x') { // xM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[10]!=~0U) _cimg_mp_return(label1pos[10]); _cimg_mp_opcode0(mp_xM); - } - if (*ss=='y') { // yM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[11]!=~0U) _cimg_mp_return(label1pos[11]); _cimg_mp_opcode0(mp_yM); - } - if (*ss=='z') { // zM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[12]!=~0U) _cimg_mp_return(label1pos[12]); _cimg_mp_opcode0(mp_zM); - } - if (*ss=='c') { // cM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (label1pos[13]!=~0U) _cimg_mp_return(label1pos[13]); _cimg_mp_opcode0(mp_cM); - } - } - } + unsigned int opcode7(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) { + if (mempos>=mem._width) mem.resize(-200,2,1,1,0); + const unsigned int pos = mempos++; + CImg::vector((uptrT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); + return pos; + } - // 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,(unsigned int)(s - ss + 1)); - variable_name.back() = 0; - bool is_valid_name = true; - if (*ss>='0' && *ss<='9') is_valid_name = false; - else for (const char *ns = ss + 1; ns'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') { - is_valid_name = false; break; - } - if (!is_valid_name) { - *se = saved_char; - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Invalid variable name '%s' in specified expression " - "'%s%s%s'.", - pixel_type(),calling_function, - variable_name._data, - (ss - 8)>expr._data?"...":"", - (ss - 8)>expr._data?ss - 8:expr._data, - se<&expr.back()?"...":""); - } - const unsigned int pos = compile(s + 1,se); + // Evaluation functions, known by the parser. + // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(uptrT), + // 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]] - // Check for particular case of a reserved variable. - if (variable_name[0] && variable_name[1] && !variable_name[2]) { - const char c1 = variable_name[0], c2 = variable_name[1]; - if (c1=='p' && c2=='i') variable_name.fill((char)0,(char)0); // pi - else if (c1=='i') { - if (c2=='m') variable_name.fill(1,0); // im - else if (c2=='M') variable_name.fill(2,0); // iM - else if (c2=='a') variable_name.fill(3,0); // ia - else if (c2=='v') variable_name.fill(4,0); // iv - else if (c2=='c') variable_name.fill(5,0); // ic - } else if (c2=='m') { - if (c1=='x') variable_name.fill(6,0); // xm - else if (c1=='y') variable_name.fill(7,0); // ym - else if (c1=='z') variable_name.fill(8,0); // zm - else if (c1=='c') variable_name.fill(9,0); // cm - } else if (c2=='M') { - if (c1=='x') variable_name.fill(10,0); // xM - else if (c1=='y') variable_name.fill(11,0); // yM - else if (c1=='z') variable_name.fill(12,0); // zM - else if (c1=='c') variable_name.fill(13,0); // cM - } - } - if (variable_name[1]) { // Multi-char variable. - int label_pos = -1; - cimglist_for(labelM,i) // Check for existing variable with same name. - if (!std::strcmp(variable_name,labelM[i])) { label_pos = i; break; } - if (label_pos<0) { // If new variable. - if (labelM._width>=labelMpos._width) labelMpos.resize(-200,1,1,1,0); - label_pos = labelM.width(); - variable_name.move_to(labelM); - } - labelMpos[label_pos] = pos; - } else label1pos[*variable_name] = pos; // Single-char variable. - _cimg_mp_return(pos); - } + static double mp_abs(_cimg_math_parser& mp) { + return cimg::abs(_mp_arg(2)); + } - // 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) { - const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s + 2,se); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_logical_or),pos,mem_A,mem_B,code._width - bp1).move_to(code,bp1); - _cimg_mp_return(pos); - } - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { - const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s + 2,se); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_logical_and),pos,mem_A,mem_B,code._width - bp1).move_to(code,bp1); - _cimg_mp_return(pos); - } - for (char *s = se2; s>ss; --s) - if (*s=='|' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_bitwise_or,compile(ss,s),compile(s + 1,se)); - for (char *s = se2; s>ss; --s) - if (*s=='&' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_bitwise_and,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(mp_noteq,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(mp_eqeq,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(mp_infeq,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(mp_supeq,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(mp_inf,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(mp_sup,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(mp_lsl,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(mp_lsr,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(mp_add,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(mp_sub,compile(ss,s),compile(s + 1,se)); - for (char *s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { - const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s + 1,se); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_mul),pos,mem_A,mem_B,code._width - bp1).move_to(code,bp1); - _cimg_mp_return(pos); - } - for (char *s = se2; s>ss; --s) - if (*s=='/' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_div,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(mp_modulo,compile(ss,s),compile(s + 1,se)); - if (ssss; --s) - if (*s=='^' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_pow,compile(ss,s),compile(s + 1,se)); + static double mp_add(_cimg_math_parser& mp) { + return _mp_arg(2) + _mp_arg(3); + } - // Look for a function call or a parenthesis. - if (*se1==']') { - const bool is_relative = *ss=='j'; - if ((*ss=='i' || is_relative) && *ss1=='[') { - if (*ss2==']') _cimg_mp_opcode0(mp_i); - _cimg_mp_opcode1(is_relative?mp_joff:mp_ioff,compile(ss2,se1)); - } + 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(2); + const unsigned int nb_args = mp.opcode._height - 2, ind = _ind<0?_ind + nb_args:(unsigned int)_ind; + if (ind>=nb_args) return 0; + return _mp_arg(ind + 2); + } + + static double mp_argmin(_cimg_math_parser& mp) { + double val = _mp_arg(2); + unsigned int argval = 0; + for (unsigned int i = 3; i=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_if),pos,mem_cond,mem_A,mem_B,bp2 - bp1,code._width - bp2). - move_to(code,bp1); - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"round(",6)) { - unsigned int value = 0, round = 1, direction = 0; - char *s1 = ss6; while (s1 opcode; - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(*ss=='a'?mp_arg:*ss=='k'?mp_kth:ss[1]=='i'?mp_min: - ss[1]=='a'?mp_max:mp_med),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,"isin(",5)) { - CImgList opcode; - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_isin),pos).move_to(opcode); - for (char *s = ss5; 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(mp_isnan,compile(ss6,se1)); - if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(mp_isinf,compile(ss6,se1)); - if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(mp_isint,compile(ss6,se1)); - if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(mp_isbool,compile(ss7,se1)); - if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { - unsigned int value = 0, nb = 1; - char *s1 = ss4; while (s1val) { val = _val; argval = i - 2; } } + return (double)argval; + } - // No known item found, assuming this is an already initialized variable. - CImg variable_name(ss,(unsigned int)(se - ss + 1)); - variable_name.back() = 0; - if (variable_name[1]) { // Multi-char variable. - cimglist_for(labelM,i) if (!std::strcmp(variable_name,labelM[i])) _cimg_mp_return(labelMpos[i]); - } else if (label1pos[*variable_name]!=~0U) _cimg_mp_return(label1pos[*variable_name]); // Single-char variable. - *se = saved_char; - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Invalid item '%s' in specified expression '%s%s%s'.\n", - pixel_type(),calling_function, - variable_name._data, - (ss - 8)>expr._data?"...":"", - (ss - 8)>expr._data?ss - 8:expr._data, - se<&expr.back()?"...":""); + static double mp_asin(_cimg_math_parser& mp) { + return std::asin(_mp_arg(2)); } - // Evaluation functions, known by the parser. - // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulong), so we can store pointers to them - // directly in the opcode vectors. - static double mp_u(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] + cimg::rand()*(mp.mem[mp.opcode(3)] - mp.mem[mp.opcode(2)]); + static double mp_atan(_cimg_math_parser& mp) { + return std::atan(_mp_arg(2)); } - static double mp_g(_cimg_math_parser& mp) { - cimg::unused(mp); - return cimg::grand(); + + static double mp_atan2(_cimg_math_parser& mp) { + return std::atan2(_mp_arg(2),_mp_arg(3)); } - static double mp_i(_cimg_math_parser& mp) { - return (double)mp.reference.atXYZC((int)mp.mem[9],(int)mp.mem[10],(int)mp.mem[11],(int)mp.mem[12],0); + + static double mp_bitwise_and(_cimg_math_parser& mp) { + return (double)((unsigned long)_mp_arg(2) & (unsigned long)_mp_arg(3)); } - static double mp_logical_and(_cimg_math_parser& mp) { - const bool is_A = (bool)mp.mem[mp.opcode(2)]; - const CImg *const pE = ++mp.p_code + mp.opcode(4); - if (!is_A) { mp.p_code = pE - 1; return 0; } - const unsigned int mem_B = (unsigned int)mp.opcode(3); - for ( ; mp.p_code &op = *mp.p_code; + + static double mp_bitwise_not(_cimg_math_parser& mp) { + return (double)~(unsigned long)_mp_arg(2); + } + + static double mp_bitwise_or(_cimg_math_parser& mp) { + return (double)((unsigned long)_mp_arg(2) | (unsigned long)_mp_arg(3)); + } + + static double mp_cbrt(_cimg_math_parser& mp) { + return std::pow(_mp_arg(2),1.0/3); + } + + 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_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) { +#ifdef cimg_use_openmp + const unsigned int n_thread = omp_get_thread_num(); +#else + const unsigned int n_thread = 0; +#endif + CImg expr(mp.opcode._height - 3); + const uptrT *ptrs = mp.opcode._data + 3; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(expr); + const uptrT g_target = mp.opcode[1]; + 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[2],(unsigned int)g_target,mp.mem._width); + std::fflush(cimg::output()); + const CImg *const p_end = (++mp.p_code) + mp.opcode[2]; + CImg _op; + mp.debug_indent+=3; + for ( ; mp.p_code &op = *mp.p_code; mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; + + _op.assign(1,op._height - 1); + const uptrT *ptrs = op._data + 1; + for (uptrT *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()); + } + 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 (double)(bool)mp.mem[mem_B]; + return mp.mem[g_target]; } - static double mp_logical_or(_cimg_math_parser& mp) { - const bool is_A = (bool)mp.mem[mp.opcode(2)]; - const CImg *const pE = ++mp.p_code + mp.opcode(4); - if (is_A) { mp.p_code = pE - 1; return 1; } - const unsigned int mem_B = (unsigned int)mp.opcode(3); - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } + + static double mp_decrement(_cimg_math_parser& mp) { + return _mp_arg(2) - 1; + } + + static double mp_dowhile(_cimg_math_parser& mp) { + const uptrT + mem_proc = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_proc = ++mp.p_code, + *const p_end = p_proc + mp.opcode[3]; + do { + for (mp.p_code = p_proc; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + } while (mp.mem[mem_cond]); --mp.p_code; - return (double)(bool)mp.mem[mem_B]; + return mp.mem[mem_proc]; } - static double mp_infeq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]<=mp.mem[mp.opcode(3)]); + + static double mp_div(_cimg_math_parser& mp) { + return _mp_arg(2)/_mp_arg(3); } - static double mp_supeq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]>=mp.mem[mp.opcode(3)]); + + static double mp_eq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)==_mp_arg(3)); } - static double mp_noteq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]!=mp.mem[mp.opcode(3)]); + + static double mp_exp(_cimg_math_parser& mp) { + return std::exp(_mp_arg(2)); } - static double mp_eqeq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]==mp.mem[mp.opcode(3)]); + + static double mp_g(_cimg_math_parser& mp) { + cimg::unused(mp); + return cimg::grand(); } - static double mp_inf(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]mp.mem[mp.opcode(3)]); + + static double mp_gt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>_mp_arg(3)); } - static double mp_add(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] + mp.mem[mp.opcode(3)]; + + static double mp_gte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>=_mp_arg(3)); } - static double mp_sub(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] - mp.mem[mp.opcode(3)]; + + static double mp_hypot(_cimg_math_parser& mp) { + double x = cimg::abs(_mp_arg(2)), y = cimg::abs(_mp_arg(3)), t; + if (x0) { t/=x; return x*std::sqrt(1+t*t); } + return 0; } - static double mp_mul(_cimg_math_parser& mp) { - const double A = mp.mem[mp.opcode(2)]; - const CImg *const pE = ++mp.p_code + mp.opcode(4); - if (!A) { mp.p_code = pE - 1; return 0; } - const unsigned int mem_B = (unsigned int)mp.opcode(3); - for ( ; mp.p_code &op = *mp.p_code; + + static double mp_i(_cimg_math_parser& mp) { + return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_x],(int)mp.mem[_cimg_mp_y], + (int)mp.mem[_cimg_mp_z],(int)mp.mem[_cimg_mp_c],0); + } + + static double mp_if(_cimg_math_parser& mp) { + const bool is_cond = (bool)_mp_arg(2); + const uptrT + 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]; + if (is_cond) { + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + mp.p_code = p_end - 1; + return mp.mem[mem_left]; + } + for (mp.p_code = p_right; mp.p_code &op = *mp.p_code; mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; + const uptrT target = mp.opcode[1]; mp.mem[target] = _cimg_mp_defunc(mp); } --mp.p_code; - return A*(double)mp.mem[mem_B]; + return mp.mem[mem_right]; } - static double mp_div(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] / mp.mem[mp.opcode(3)]; + + static double mp_increment(_cimg_math_parser& mp) { + return _mp_arg(2) + 1; } - static double mp_minus(_cimg_math_parser& mp) { - return -mp.mem[mp.opcode(2)]; + + static double mp_int(_cimg_math_parser& mp) { + return (double)(long)_mp_arg(2); } - static double mp_logical_not(_cimg_math_parser& mp) { - return !mp.mem[mp.opcode(2)]; + + static double mp_ioff(_cimg_math_parser& mp) { + const long off = (long)_mp_arg(2); + const unsigned int boundary_conditions = (unsigned int)_mp_arg(3); + if (off<0 || off>=(long)mp.imgin.size()) + switch (boundary_conditions) { + case 2 : // Periodic boundary. + if (mp.imgin) return (double)mp.imgin[cimg::mod(off,(long)mp.imgin.size())]; + return 0; + case 1 : // Neumann boundary. + if (mp.imgin) return (double)(off<0?*mp.imgin:mp.imgin.back()); + return 0; + default : // Dirichet boundary. + return 0; + } + return (double)mp.imgin[off]; } - static double mp_bitwise_not(_cimg_math_parser& mp) { - return ~(unsigned long)mp.mem[mp.opcode(2)]; + + static double mp_isbool(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return (double)(val==0.0 || val==1.0); } - static double mp_modulo(_cimg_math_parser& mp) { - return cimg::mod(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)]); + + static double mp_isin(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + for (unsigned int i = 3; i::is_inf(_mp_arg(2)); } - static double mp_bitwise_or(_cimg_math_parser& mp) { - return ((unsigned long)mp.mem[mp.opcode(2)] | (unsigned long)mp.mem[mp.opcode(3)]); + + static double mp_isint(_cimg_math_parser& mp) { + return (double)(cimg::mod(_mp_arg(2),1.0)==0); } - static double mp_pow(_cimg_math_parser& mp) { - const double v = mp.mem[mp.opcode(2)], p = mp.mem[mp.opcode(3)]; - if (p==0) return 1; - if (p==0.5) return std::sqrt(v); - if (p==1) return v; - if (p==2) return v*v; - if (p==3) return v*v*v; - if (p==4) return v*v*v*v; - return std::pow(v,p); + + static double mp_isnan(_cimg_math_parser& mp) { + return (double)cimg::type::is_nan(_mp_arg(2)); } - static double mp_sin(_cimg_math_parser& mp) { - return std::sin(mp.mem[mp.opcode(2)]); + + static double mp_ixyzc(_cimg_math_parser& mp) { + const double + x = _mp_arg(2), y = _mp_arg(3), + z = _mp_arg(4), c = _mp_arg(5); + const unsigned int + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + if (interpolation==0) { // Nearest neighbor interpolation. + if (boundary_conditions==2) + return (double)mp.imgin.atXYZC(cimg::mod((int)x,mp.imgin.width()), + cimg::mod((int)y,mp.imgin.height()), + cimg::mod((int)z,mp.imgin.depth()), + cimg::mod((int)c,mp.imgin.spectrum())); + if (boundary_conditions==1) + return (double)mp.imgin.atXYZC((int)x,(int)y,(int)z,(int)c); + return (double)mp.imgin.atXYZC((int)x,(int)y,(int)z,(int)c,0); + } else { // Linear interpolation. + if (boundary_conditions==2) + return (double)mp.imgin.linear_atXYZC(cimg::mod((float)x,(float)mp.imgin.width()), + cimg::mod((float)y,(float)mp.imgin.height()), + cimg::mod((float)z,(float)mp.imgin.depth()), + cimg::mod((float)c,(float)mp.imgin.spectrum())); + if (boundary_conditions==1) + return (double)mp.imgin.linear_atXYZC((float)x,(float)y,(float)z,(float)c); + return (double)mp.imgin.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); + } } - static double mp_cos(_cimg_math_parser& mp) { - return std::cos(mp.mem[mp.opcode(2)]); + + static double mp_joff(_cimg_math_parser& mp) { + const int + x = (int)mp.mem[_cimg_mp_x], y = (int)mp.mem[_cimg_mp_y], + z = (int)mp.mem[_cimg_mp_z], c = (int)mp.mem[_cimg_mp_c]; + const long off = mp.imgin.offset(x,y,z,c) + (long)_mp_arg(2); + const unsigned int boundary = (unsigned int)_mp_arg(3); + if (off<0 || off>=(long)mp.imgin.size()) + switch (boundary) { + case 2 : // Periodic boundary. + if (mp.imgin) return (double)mp.imgin[cimg::mod(off,(long)mp.imgin.size())]; + return 0; + case 1 : // Neumann boundary. + if (mp.imgin) return (double)(off<0?*mp.imgin:mp.imgin.back()); + return 0; + default : // Dirichet boundary. + return 0; + } + return (double)mp.imgin[off]; } - static double mp_tan(_cimg_math_parser& mp) { - return std::tan(mp.mem[mp.opcode(2)]); + + static double mp_jxyzc(_cimg_math_parser& mp) { + const double + x = mp.mem[_cimg_mp_x], y = mp.mem[_cimg_mp_y], z = mp.mem[_cimg_mp_z], c = mp.mem[_cimg_mp_c], + nx = x + _mp_arg(2), ny = y + _mp_arg(3), + nz = z + _mp_arg(4), nc = c + _mp_arg(5); + const unsigned int + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + if (interpolation==0) { // Nearest neighbor interpolation. + if (boundary_conditions==2) + return (double)mp.imgin.atXYZC(cimg::mod((int)nx,mp.imgin.width()), + cimg::mod((int)ny,mp.imgin.height()), + cimg::mod((int)nz,mp.imgin.depth()), + cimg::mod((int)nc,mp.imgin.spectrum())); + if (boundary_conditions==1) + return (double)mp.imgin.atXYZC((int)nx,(int)ny,(int)nz,(int)nc); + return (double)mp.imgin.atXYZC((int)nx,(int)ny,(int)nz,(int)nc,0); + } else { // Linear interpolation. + if (boundary_conditions==2) + return (double)mp.imgin.linear_atXYZC(cimg::mod((float)nx,(float)mp.imgin.width()), + cimg::mod((float)ny,(float)mp.imgin.height()), + cimg::mod((float)nz,(float)mp.imgin.depth()), + cimg::mod((float)nc,(float)mp.imgin.spectrum())); + if (boundary_conditions==1) + return (double)mp.imgin.linear_atXYZC((float)nx,(float)ny,(float)nz,(float)nc); + return (double)mp.imgin.linear_atXYZC((float)nx,(float)ny,(float)nz,(float)nc,0); + } } - static double mp_asin(_cimg_math_parser& mp) { - return std::asin(mp.mem[mp.opcode(2)]); + + static double mp_kth(_cimg_math_parser& mp) { + CImg vals(mp.opcode._height - 3); + double *p = vals.data(); + for (unsigned int i = 3; i &img = mp.listin[ind]; + if (off<0 || off>=(long)img.size()) + switch (boundary_conditions) { + case 2 : // Periodic boundary. + if (img) return (double)img[cimg::mod(off,(long)mp.imgin.size())]; + return 0; + case 1 : // Neumann boundary. + if (img) return (double)(off<0?*img:img.back()); + return 0; + default : // Dirichet boundary. + return 0; + } + return (double)img[off]; + } + + static double mp_list_is_shared(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._is_shared; + } + + static double mp_list_ixyzc(_cimg_math_parser& mp) { + if (!mp.listin) return 0; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(7), + boundary_conditions = (unsigned int)_mp_arg(8); + const double + x = _mp_arg(3), y = _mp_arg(4), + z = _mp_arg(5), c = _mp_arg(6); + const CImg &img = mp.listin[ind]; + if (interpolation==0) { // Nearest neighbor interpolation. + if (boundary_conditions==2) + return (double)img.atXYZC(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + cimg::mod((int)c,img.spectrum())); + if (boundary_conditions==1) + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); + } else { // Linear interpolation. + if (boundary_conditions==2) + return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()), + cimg::mod((float)c,(float)img.spectrum())); + if (boundary_conditions==1) + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); + } } - static double mp_cosh(_cimg_math_parser& mp) { - return std::cosh(mp.mem[mp.opcode(2)]); + + static double mp_list_joff(_cimg_math_parser& mp) { + if (!mp.listin) return 0; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4); + const int + x = (int)mp.mem[_cimg_mp_x], y = (int)mp.mem[_cimg_mp_y], + z = (int)mp.mem[_cimg_mp_z], c = (int)mp.mem[_cimg_mp_c]; + const CImg &img = mp.listin[ind]; + const long off = img.offset(x,y,z,c) + (long)_mp_arg(3); + if (off<0 || off>=(long)img.size()) + switch (boundary_conditions) { + case 2 : // Periodic boundary. + if (img) return (double)img(ind,cimg::mod(off,(long)img.size())); + return 0; + case 1 : // Neumann boundary. + if (img) return (double)(off<0?*img:img.back()); + return 0; + default : // Dirichet boundary. + return 0; + } + return (double)img[off]; + } + + static double mp_list_jxyzc(_cimg_math_parser& mp) { + if (!mp.listin) return 0; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(7), + boundary_conditions = (unsigned int)_mp_arg(8); + const double + x = mp.mem[_cimg_mp_x], y = mp.mem[_cimg_mp_y], z = mp.mem[_cimg_mp_z], c = mp.mem[_cimg_mp_c], + nx = x + _mp_arg(3), ny = y + _mp_arg(4), + nz = z + _mp_arg(5), nc = c + _mp_arg(6); + const CImg &img = mp.listin[ind]; + if (interpolation==0) { // Nearest neighbor interpolation. + if (boundary_conditions==2) + return (double)img.atXYZC(cimg::mod((int)nx,img.width()), + cimg::mod((int)ny,img.height()), + cimg::mod((int)nz,img.depth()), + cimg::mod((int)nc,img.spectrum())); + if (boundary_conditions==1) + return (double)img.atXYZC((int)nx,(int)ny,(int)nz,(int)nc); + return (double)img.atXYZC((int)nx,(int)ny,(int)nz,(int)nc,0); + } else { // Linear interpolation. + if (boundary_conditions==2) + return (double)img.linear_atXYZC(cimg::mod((float)nx,(float)img.width()), + cimg::mod((float)ny,(float)img.height()), + cimg::mod((float)nz,(float)img.depth()), + cimg::mod((float)nc,(float)img.spectrum())); + if (boundary_conditions==1) return (double)img.linear_atXYZC((float)nx,(float)ny,(float)nz,(float)nc); + return (double)img.linear_atXYZC((float)nx,(float)ny,(float)nz,(float)nc,0); + } + } + + static double mp_list_median(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + if (!mp.list_median) mp.list_median.assign(mp.listin._width); + if (!mp.list_median[ind]) CImg::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 double val = _mp_arg(4); + if (!mp.listout) return val; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const long off = (long)_mp_arg(3); + CImg &img = mp.listout[ind]; + if (off>=0 && off<(long)img.size()) img._data[off] = (T)val; + return val; } - static double mp_tanh(_cimg_math_parser& mp) { - return std::tanh(mp.mem[mp.opcode(2)]); + + static double mp_list_set_ixyzc(_cimg_math_parser& mp) { + const double val = _mp_arg(7); + if (!mp.listout) return val; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const int + x = (int)_mp_arg(3), y = (int)_mp_arg(4), + z = (int)_mp_arg(5), c = (int)_mp_arg(6); + CImg &img = mp.listout[ind]; + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const long off = img.offset(x,y,z,c) + (long)_mp_arg(3); + if (off>=0 && off<(long)img.size()) img[off] = (T)val; + return val; } - static double mp_log2(_cimg_math_parser& mp) { - return cimg::log2(mp.mem[mp.opcode(2)]); + + static double mp_list_set_jxyzc(_cimg_math_parser& mp) { + const double val = _mp_arg(7); + if (!mp.listout) return val; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const double x = mp.mem[_cimg_mp_x], y = mp.mem[_cimg_mp_y], z = mp.mem[_cimg_mp_z], c = mp.mem[_cimg_mp_c]; + const int + nx = (int)(x + _mp_arg(3)), ny = (int)(y + _mp_arg(4)), + nz = (int)(z + _mp_arg(5)), nc = (int)(c + _mp_arg(6)); + CImg &img = mp.listout[ind]; + if (nx>=0 && nx=0 && ny=0 && nz=0 && nc - *const pB = ++mp.p_code + mp.opcode(5), - *const pE = pB + mp.opcode(6); - if (is_cond) { // Evaluate on-the-fly only the correct argument. - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - mp.p_code = pE - 1; - return mp.mem[mem_A]; - } - for (mp.p_code = pB; mp.p_code &op = *mp.p_code; + + 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 uptrT mem_right = mp.opcode[3]; + for ( ; mp.p_code &op = *mp.p_code; mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; + const uptrT target = mp.opcode[1]; mp.mem[target] = _cimg_mp_defunc(mp); } --mp.p_code; - return mp.mem[mem_B]; + return (double)(bool)mp.mem[mem_right]; } - static double mp_round(_cimg_math_parser& mp) { - return cimg::round(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)],(int)mp.mem[mp.opcode(4)]); + + static double mp_logical_not(_cimg_math_parser& mp) { + return (double)!_mp_arg(2); } - static double mp_ixyzc(_cimg_math_parser& mp) { - const int i = (int)mp.mem[mp.opcode(6)], b = (int)mp.mem[mp.opcode(7)]; - if (i==0) { // Nearest neighbor interpolation. - if (b==2) return (double)mp.reference.atXYZC(cimg::mod((int)mp.mem[mp.opcode(2)],mp.reference.width()), - cimg::mod((int)mp.mem[mp.opcode(3)],mp.reference.height()), - cimg::mod((int)mp.mem[mp.opcode(4)],mp.reference.depth()), - cimg::mod((int)mp.mem[mp.opcode(5)],mp.reference.spectrum())); - if (b==1) return (double)mp.reference.atXYZC((int)mp.mem[mp.opcode(2)], - (int)mp.mem[mp.opcode(3)], - (int)mp.mem[mp.opcode(4)], - (int)mp.mem[mp.opcode(5)]); - return (double)mp.reference.atXYZC((int)mp.mem[mp.opcode(2)], - (int)mp.mem[mp.opcode(3)], - (int)mp.mem[mp.opcode(4)], - (int)mp.mem[mp.opcode(5)],0); - } else { // Linear interpolation. - if (b==2) return (double)mp.reference.linear_atXYZC(cimg::mod((float)mp.mem[mp.opcode(2)], - (float)mp.reference.width()), - cimg::mod((float)mp.mem[mp.opcode(3)], - (float)mp.reference.height()), - cimg::mod((float)mp.mem[mp.opcode(4)], - (float)mp.reference.depth()), - cimg::mod((float)mp.mem[mp.opcode(5)], - (float)mp.reference.spectrum())); - if (b==1) return (double)mp.reference.linear_atXYZC((float)mp.mem[mp.opcode(2)], - (float)mp.mem[mp.opcode(3)], - (float)mp.mem[mp.opcode(4)], - (float)mp.mem[mp.opcode(5)]); - return (double)mp.reference.linear_atXYZC((float)mp.mem[mp.opcode(2)], - (float)mp.mem[mp.opcode(3)], - (float)mp.mem[mp.opcode(4)], - (float)mp.mem[mp.opcode(5)],0); + + 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 uptrT mem_right = mp.opcode[3]; + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; } - static double mp_jxyzc(_cimg_math_parser& mp) { - const double x = mp.mem[9], y = mp.mem[10], z = mp.mem[11], c = mp.mem[12]; - const int i = (int)mp.mem[mp.opcode(6)], b = (int)mp.mem[mp.opcode(7)]; - if (i==0) { // Nearest neighbor interpolation. - if (b==2) return (double)mp.reference.atXYZC(cimg::mod((int)(x + mp.mem[mp.opcode(2)]), - mp.reference.width()), - cimg::mod((int)(y + mp.mem[mp.opcode(3)]), - mp.reference.height()), - cimg::mod((int)(z + mp.mem[mp.opcode(4)]), - mp.reference.depth()), - cimg::mod((int)(c + mp.mem[mp.opcode(5)]), - mp.reference.spectrum())); - if (b==1) return (double)mp.reference.atXYZC((int)(x + mp.mem[mp.opcode(2)]), - (int)(y + mp.mem[mp.opcode(3)]), - (int)(z + mp.mem[mp.opcode(4)]), - (int)(c + mp.mem[mp.opcode(5)])); - return (double)mp.reference.atXYZC((int)(x + mp.mem[mp.opcode(2)]), - (int)(y + mp.mem[mp.opcode(3)]), - (int)(z + mp.mem[mp.opcode(4)]), - (int)(c + mp.mem[mp.opcode(5)]),0); - } else { // Linear interpolation. - if (b==2) return (double)mp.reference.linear_atXYZC(cimg::mod((float)(x + mp.mem[mp.opcode(2)]), - (float)mp.reference.width()), - cimg::mod((float)(y + mp.mem[mp.opcode(3)]), - (float)mp.reference.height()), - cimg::mod((float)(z + mp.mem[mp.opcode(4)]), - (float)mp.reference.depth()), - cimg::mod((float)(c + mp.mem[mp.opcode(5)]), - (float)mp.reference.spectrum())); - if (b==1) return (double)mp.reference.linear_atXYZC((float)(x + mp.mem[mp.opcode(2)]), - (float)(y + mp.mem[mp.opcode(3)]), - (float)(z + mp.mem[mp.opcode(4)]), - (float)(c + mp.mem[mp.opcode(5)])); - return (double)mp.reference.linear_atXYZC((float)(x + mp.mem[mp.opcode(2)]), - (float)(y + mp.mem[mp.opcode(3)]), - (float)(z + mp.mem[mp.opcode(4)]), - (float)(c + mp.mem[mp.opcode(5)]),0); - } + + static double mp_bitwise_left_shift(_cimg_math_parser& mp) { + return (double)((long)_mp_arg(2)<<(unsigned int)_mp_arg(3)); } - static double mp_min(_cimg_math_parser& mp) { - double val = mp.mem[mp.opcode(2)]; - for (unsigned int i = 3; i>(unsigned int)_mp_arg(3)); + } + + 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_max(_cimg_math_parser& mp) { - double val = mp.mem[mp.opcode(2)]; - for (unsigned int i = 3; i values(mp.opcode._height - 2); - double *p = values.data(); - for (unsigned int i = 2; i vals(mp.opcode._height - 2); + double *p = vals.data(); + for (unsigned int i = 2; i values(mp.opcode._height - 3); - double *p = values.data(); - for (unsigned int i = 3; ires) res = val; + } + return res; + } + + static double mp_normp(_cimg_math_parser& mp) { + const double p = (double)mp.opcode[2]; + double res = 0; for (unsigned int i = 3; i0?res:0.0; } - static double mp_isnan(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return cimg::type::is_nan(val); + + 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_isinf(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return cimg::type::is_inf(val); + + static double mp_pow3(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val; } - static double mp_isint(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return (double)(cimg::mod(val,1.0)==0); + + static double mp_pow4(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val*val; } - static double mp_isbool(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return (val==0.0 || val==1.0); + + static double mp_print(_cimg_math_parser& mp) { + CImg expr(mp.opcode._height - 2); + const uptrT *ptrs = mp.opcode._data + 2; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(expr); + const double val = _mp_arg(1); + std::fprintf(cimg::output(),"\n[_cimg_math_parser] '%s' = %g",expr._data,val); + std::fflush(cimg::output()); + return val; + } + + static double mp_replace(_cimg_math_parser& mp) { + return _mp_arg(2); } + static double mp_rol(_cimg_math_parser& mp) { - return cimg::rol(mp.mem[mp.opcode(2)],(unsigned int)mp.mem[mp.opcode(3)]); + return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3)); } + static double mp_ror(_cimg_math_parser& mp) { - return cimg::ror(mp.mem[mp.opcode(2)],(unsigned int)mp.mem[mp.opcode(3)]); + return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3)); } - static double mp_lsl(_cimg_math_parser& mp) { - return (long)mp.mem[mp.opcode(2)]<<(unsigned int)mp.mem[mp.opcode(3)]; + + static double mp_round(_cimg_math_parser& mp) { + return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); } - static double mp_lsr(_cimg_math_parser& mp) { - return (long)mp.mem[mp.opcode(2)]>>(unsigned int)mp.mem[mp.opcode(3)]; + + static double mp_self_add(_cimg_math_parser& mp) { + return _mp_arg(1)+=_mp_arg(2); } - static double mp_sinc(_cimg_math_parser& mp) { - return cimg::sinc(mp.mem[mp.opcode(2)]); + + static double mp_self_bitwise_and(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((unsigned long)val & (unsigned long)_mp_arg(2)); } - static double mp_im(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[0]:0; + + static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((long)val<<(unsigned int)_mp_arg(2)); } - static double mp_iM(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[1]:0; + + static double mp_self_bitwise_or(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((unsigned long)val | (unsigned long)_mp_arg(2)); } - static double mp_ia(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[2]:0; + + static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((long)val>>(unsigned int)_mp_arg(2)); } - static double mp_iv(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[3]:0; + + static double mp_self_decrement(_cimg_math_parser& mp) { + return --_mp_arg(1); } - static double mp_ic(_cimg_math_parser& mp) { - return mp.is_median_value?mp.median_value:0; + + static double mp_self_increment(_cimg_math_parser& mp) { + return ++_mp_arg(1); } - static double mp_xm(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[4]:0; + + static double mp_self_mul(_cimg_math_parser& mp) { + return _mp_arg(1)*=_mp_arg(2); } - static double mp_ym(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[5]:0; + + static double mp_self_div(_cimg_math_parser& mp) { + return _mp_arg(1)/=_mp_arg(2); } - static double mp_zm(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[6]:0; + + 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_cm(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[7]:0; + + static double mp_self_power(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = std::pow(val,_mp_arg(2)); } - static double mp_xM(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[8]:0; + + static double mp_self_sub(_cimg_math_parser& mp) { + return _mp_arg(1)-=_mp_arg(2); } - static double mp_yM(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[9]:0; + + static double mp_set_ioff(_cimg_math_parser& mp) { + const long off = (long)_mp_arg(2); + const double val = _mp_arg(3); + if (off>=0 && off<(long)mp.imgout.size()) mp.imgout[off] = (T)val; + return val; } - static double mp_zM(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[10]:0; + + static double mp_set_ixyzc(_cimg_math_parser& mp) { + 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(6); + if (x>=0 && x=0 && y=0 && z=0 && c=0 && off<(long)mp.imgout.size()) mp.imgout[off] = (T)val; + return val; } - static double mp_arg(_cimg_math_parser& mp) { - const int _ind = (int)mp.mem[mp.opcode(2)]; - const unsigned int nb_args = mp.opcode._height - 2, ind = _ind<0?_ind + nb_args:(unsigned int)_ind; - if (ind>=nb_args) return 0; - return mp.mem[mp.opcode(ind + 2)]; + + static double mp_set_jxyzc(_cimg_math_parser& mp) { + const double x = mp.mem[_cimg_mp_x], y = mp.mem[_cimg_mp_y], z = mp.mem[_cimg_mp_z], c = mp.mem[_cimg_mp_c]; + const int + nx = (int)(x + _mp_arg(2)), ny = (int)(y + _mp_arg(3)), + nz = (int)(z + _mp_arg(4)), nc = (int)(c + _mp_arg(5)); + const double val = _mp_arg(6); + if (nx>=0 && nx=0 && y=0 && nz=0 && c=mp.reference.size()) return 0; - return (double)mp.reference[off]; + + static double mp_sin(_cimg_math_parser& mp) { + return std::sin(_mp_arg(2)); } - static double mp_joff(_cimg_math_parser& mp) { - const int x = (int)mp.mem[9], y = (int)mp.mem[10], z = (int)mp.mem[11], c = (int)mp.mem[12]; - const unsigned long off = mp.reference.offset(x,y,z,c) + (unsigned long)(mp.mem[mp.opcode(2)]); - if (off>=mp.reference.size()) return 0; - return (double)mp.reference[off]; + + static double mp_sinc(_cimg_math_parser& mp) { + return cimg::sinc(_mp_arg(2)); } - // Evaluation procedure, with image data. - double operator()(const double x, const double y, const double z, const double c) { - if (!mem) return 0; - mem[9] = x; mem[10] = y; mem[11] = z; mem[12] = c; - opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1; + static double mp_sinh(_cimg_math_parser& mp) { + return std::sinh(_mp_arg(2)); + } - for (p_code = code._data; p_code &op = *p_code; - // Allows to avoid parameter passing to evaluation functions. - opcode._data = op._data; opcode._height = op._height; - const unsigned int target = (unsigned int)opcode[1]; - mem[target] = _cimg_mp_defunc(*this); - } - return mem[result]; + 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_sub(_cimg_math_parser& mp) { + return _mp_arg(2) - _mp_arg(3); + } + + static double mp_tan(_cimg_math_parser& mp) { + return std::tan(_mp_arg(2)); + } + + static double mp_tanh(_cimg_math_parser& mp) { + return std::tanh(_mp_arg(2)); + } + + static double mp_u(_cimg_math_parser& mp) { + return cimg::rand(_mp_arg(2),_mp_arg(3)); + } + + static double mp_whiledo(_cimg_math_parser& mp) { // Used also by 'for()'. + const uptrT + mem_proc = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_cond = ++mp.p_code, + *const p_proc = p_cond + mp.opcode[3], + *const p_end = p_proc + mp.opcode[4]; + bool is_first_iter = true, is_cond = false; + do { + for (mp.p_code = p_cond; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond) { // Evaluate loop iteration. + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + is_first_iter = false; + } + } while (is_cond); + mp.p_code = p_end - 1; + return is_first_iter?0:mp.mem[mem_proc]; + } + +#undef _mp_arg + + }; // struct _cimg_math_parser {}. //! Compute the square value of each pixel value. /** @@ -14727,7 +16600,7 @@ namespace cimg_library_suffixed { CImg& sqr() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if (size()>=524288) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); }; return *this; @@ -14754,7 +16627,7 @@ namespace cimg_library_suffixed { CImg& sqrt() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd); return *this; @@ -14775,7 +16648,7 @@ namespace cimg_library_suffixed { CImg& exp() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=4096) +#pragma omp parallel for cimg_openmp_if (size()>=4096) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd); return *this; @@ -14797,7 +16670,7 @@ namespace cimg_library_suffixed { CImg& log() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=262144) +#pragma omp parallel for cimg_openmp_if (size()>=262144) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd); return *this; @@ -14819,7 +16692,7 @@ namespace cimg_library_suffixed { CImg& log2() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=4096) +#pragma omp parallel for cimg_openmp_if (size()>=4096) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd); return *this; @@ -14841,7 +16714,7 @@ namespace cimg_library_suffixed { CImg& log10() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=4096) +#pragma omp parallel for cimg_openmp_if (size()>=4096) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd); return *this; @@ -14862,7 +16735,7 @@ namespace cimg_library_suffixed { CImg& abs() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if (size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::abs(*ptrd); return *this; @@ -14888,7 +16761,7 @@ namespace cimg_library_suffixed { CImg& sign() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::sign(*ptrd); return *this; @@ -14910,7 +16783,7 @@ namespace cimg_library_suffixed { CImg& cos() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd); return *this; @@ -14932,7 +16805,7 @@ namespace cimg_library_suffixed { CImg& sin() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd); return *this; @@ -14955,7 +16828,7 @@ namespace cimg_library_suffixed { CImg& sinc() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if (size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd); return *this; @@ -14977,7 +16850,7 @@ namespace cimg_library_suffixed { CImg& tan() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if (size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd); return *this; @@ -14999,7 +16872,7 @@ namespace cimg_library_suffixed { CImg& cosh() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if (size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd); return *this; @@ -15021,7 +16894,7 @@ namespace cimg_library_suffixed { CImg& sinh() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if (size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd); return *this; @@ -15043,7 +16916,7 @@ namespace cimg_library_suffixed { CImg& tanh() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if (size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd); return *this; @@ -15065,7 +16938,7 @@ namespace cimg_library_suffixed { CImg& acos() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd); return *this; @@ -15087,7 +16960,7 @@ namespace cimg_library_suffixed { CImg& asin() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd); return *this; @@ -15109,7 +16982,7 @@ namespace cimg_library_suffixed { CImg& atan() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd); return *this; @@ -15238,35 +17111,35 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; if (p==-4) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); } return *this; } if (p==-3) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); } return *this; } if (p==-2) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); } return *this; } if (p==-1) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); } return *this; } if (p==-0.5) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); } return *this; @@ -15277,20 +17150,20 @@ namespace cimg_library_suffixed { if (p==2) return sqr(); if (p==3) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=262144) +#pragma omp parallel for cimg_openmp_if (size()>=262144) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; } if (p==4) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=131072) +#pragma omp parallel for cimg_openmp_if (size()>=131072) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; } #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=1024) +#pragma omp parallel for cimg_openmp_if (size()>=1024) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p); return *this; @@ -15310,37 +17183,37 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"pow"); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "pow",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); --ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); --ptrd; } else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)std::pow((double)*ptrd,lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)std::pow((double)*ptrd,lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(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); + pow(CImg(_width,_height,_depth,_spectrum,expression,true)); } cimg::exception_mode(omode); return *this; @@ -15382,7 +17255,7 @@ namespace cimg_library_suffixed { CImg& rol(const unsigned int n=1) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n); return *this; @@ -15402,8 +17275,12 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"rol"); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "rol",base,this); T *ptrd = *expression=='<'?end() - 1:_data; if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); --ptrd; } @@ -15411,29 +17288,24 @@ namespace cimg_library_suffixed { cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(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); + rol(CImg(_width,_height,_depth,_spectrum,expression,true)); } cimg::exception_mode(omode); return *this; @@ -15475,7 +17347,7 @@ namespace cimg_library_suffixed { CImg& ror(const unsigned int n=1) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n); return *this; @@ -15495,8 +17367,12 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"ror"); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "ror",base,this); T *ptrd = *expression=='<'?end() - 1:_data; if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); --ptrd; } @@ -15504,29 +17380,24 @@ namespace cimg_library_suffixed { cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(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); + ror(CImg(_width,_height,_depth,_spectrum,expression,true)); } cimg::exception_mode(omode); return *this; @@ -15570,7 +17441,7 @@ namespace cimg_library_suffixed { CImg& min(const T& val) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if (size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val); return *this; @@ -15618,36 +17489,37 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"min"); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "min",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::min(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)cimg::min(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(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); + min(CImg(_width,_height,_depth,_spectrum,expression,true)); } cimg::exception_mode(omode); return *this; @@ -15667,7 +17539,7 @@ namespace cimg_library_suffixed { CImg& max(const T& val) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if (size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val); return *this; @@ -15715,36 +17587,37 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"max"); + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "max",base,this); T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); --ptrd; } + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::max(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) { *ptrd = (T)cimg::max(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } + } } - } else #endif cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(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); + max(CImg(_width,_height,_depth,_spectrum,expression,true)); } cimg::exception_mode(omode); return *this; @@ -15896,7 +17769,7 @@ namespace cimg_library_suffixed { cimg_instance); CImg arr(*this); unsigned int l = 0, ir = size() - 1; - for (;;) { + for ( ; ; ) { if (ir<=l + 1) { if (ir==l + 1 && arr[ir]arr[l + 1]) cimg::swap(arr[l],arr[l + 1]); unsigned int i = l + 1, j = ir; const T pivot = arr[l + 1]; - for (;;) { + for ( ; ; ) { do ++i; while (arr[i]pivot); if (j>1) - 1))/2); } + //! Return the product of all the pixel values. + /** + **/ + double product() const { + if (is_empty()) return 0; + double res = 1; + cimg_for(*this,ptrs,T) res*=(double)*ptrs; + return res; + } + //! Return the sum of all the pixel values. /** **/ double sum() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "sum(): Empty instance.", - cimg_instance); double res = 0; cimg_for(*this,ptrs,T) res+=(double)*ptrs; return res; @@ -15952,10 +17831,6 @@ namespace cimg_library_suffixed { /** **/ double mean() const { - if (is_empty()) - throw CImgInstanceException(_cimg_instance - "mean(): Empty instance.", - cimg_instance); double res = 0; cimg_for(*this,ptrs,T) res+=(double)*ptrs; return res/size(); @@ -16056,7 +17931,7 @@ namespace cimg_library_suffixed { if (_depth==1) { const double cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=262144 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height>=262144 && _spectrum>=2) #endif cimg_forC(*this,c) { CImg_3x3(I,T); @@ -16068,7 +17943,7 @@ namespace cimg_library_suffixed { } else { const double cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=262144 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=262144 && _spectrum>=2) #endif cimg_forC(*this,c) { CImg_3x3x3(I,T); @@ -16148,11 +18023,35 @@ namespace cimg_library_suffixed { \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 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 { + 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); + } + + //! 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 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; - return _cimg_math_parser(*this,expression,"eval")(x,y,z,c); + 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; + } + return _cimg_math_parser(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "eval",*this,output,list_inputs,list_outputs)(x,y,z,c); } //! Evaluate math formula on a set of variables. @@ -16161,10 +18060,24 @@ namespace cimg_library_suffixed { \param xyzc Set of values (x,y,z,c) used for the evaluation. **/ template - CImg eval(const char *const expression, const CImg& xyzc) const { + 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(*this,expression,"eval"); + _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs); #ifdef cimg_use_openmp #pragma omp parallel if (res._height>=512 && std::strlen(expression)>=6) { @@ -16191,14 +18104,15 @@ namespace cimg_library_suffixed { //! Compute statistics vector from the pixel values. /* \param variance_method Method used to compute the variance (see variance(const unsigned int) const). - \return Statistics vector as [min; max; mean; variance; xmin; ymin; zmin; cmin; xmax; ymax; zmax; cmax]. + \return Statistics vector as + [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 long siz = size(); const T *const odata = _data; const T *pm = odata, *pM = odata; - double S = 0, S2 = 0; + double S = 0, S2 = 0, P = _data?1:0; T m = *pm, M = m; cimg_for(*this,ptrs,T) { const T val = *ptrs; @@ -16207,6 +18121,7 @@ namespace cimg_library_suffixed { if (val>M) { M = val; pM = ptrs; } S+=_val; S2+=_val*_val; + P*=_val; } const double mean_value = S/siz, @@ -16219,9 +18134,10 @@ namespace cimg_library_suffixed { 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((double)m,(double)M,mean_value,variance_value, + 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); + (double)xM,(double)yM,(double)zM,(double)cM, + S,P); } //! Compute statistics vector from the pixel values \inplace. @@ -16707,8 +18623,8 @@ namespace cimg_library_suffixed { SVD(U,S,V); const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*cimg::max(_width,_height)*S.max(); cimg_forX(V,x) { - const Tfloat s = S(x), invs = s>tolerance?1/s:(Tfloat)0; - cimg_forY(V,y) V(x,y)*=invs; + const Tfloat s = S(x), invs = s>tolerance?1/s:(Tfloat)0; + cimg_forY(V,y) V(x,y)*=invs; } return V*U.transpose(); } @@ -16768,31 +18684,31 @@ namespace cimg_library_suffixed { #endif } else { // Least-square solution for non-square systems. #ifdef cimg_use_lapack - char TRANS = 'N'; + 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]; + 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); + 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); + 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)); + assign(A.get_pseudoinvert()*(*this)); #endif } return *this; @@ -16864,7 +18780,7 @@ namespace cimg_library_suffixed { //! 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 eigenvalues, sorted by columns. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. **/ template const CImg& eigen(CImg& val, CImg &vec) const { @@ -16920,7 +18836,7 @@ namespace cimg_library_suffixed { //! 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 eigenvalues, sorted by columns. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. **/ template const CImg& symmetric_eigen(CImg& val, CImg& vec) const { @@ -16942,7 +18858,7 @@ namespace cimg_library_suffixed { INFO); val.assign(1,N); - vec.assign(N,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]); @@ -16954,8 +18870,8 @@ namespace cimg_library_suffixed { "eigen(): Instance is not a square matrix.", cimg_instance); - val.assign(1,_width); - if (vec._data) vec.assign(_width,_width); + val.assign(1,_width); + if (vec._data) vec.assign(_width,_width); if (_width<3) { eigen(val,vec); if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices. @@ -16966,20 +18882,20 @@ namespace cimg_library_suffixed { (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. - if (val[p]>eig) eig = (float)val[p]; + bool is_ambiguous = false; + float eig = 0; + cimg_forY(val,p) { // check for ambiguous cases. + if (val[p]>eig) eig = (float)val[p]; t scal = 0; cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); if (cimg::abs(scal)<0.9f) is_ambiguous = true; if (scal<0) val[p] = -val[p]; } - if (is_ambiguous) { - ++(eig*=2); - SVD(vec,val,V,false,40,eig); - val-=eig; - } + if (is_ambiguous) { + ++(eig*=2); + SVD(vec,val,V,false,40,eig); + val-=eig; + } CImg permutations; // sort eigenvalues in decreasing order CImg tmp(_width); val.sort(permutations,false); @@ -17162,10 +19078,10 @@ namespace cimg_library_suffixed { if (is_empty()) { U.assign(); S.assign(); V.assign(); } else { U = *this; - if (lambda!=0) { - const unsigned int delta = cimg::min(U._width,U._height); - for (unsigned int i = 0; i rv1(_width); @@ -17539,7 +19455,7 @@ namespace cimg_library_suffixed { //! 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) { + const T& a4, const T& a5, const T& a6) { CImg r(1,7); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; @@ -17548,7 +19464,7 @@ namespace cimg_library_suffixed { //! 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) { + const T& a4, const T& a5, const T& a6, const T& a7) { CImg r(1,8); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; @@ -17557,8 +19473,8 @@ namespace cimg_library_suffixed { //! 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) { + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { CImg r(1,9); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; @@ -17603,7 +19519,7 @@ namespace cimg_library_suffixed { 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& a12) { CImg r(1,13); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; @@ -17616,7 +19532,7 @@ namespace cimg_library_suffixed { 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& a12, const T& a13) { CImg r(1,14); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; @@ -17629,7 +19545,7 @@ namespace cimg_library_suffixed { 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& a12, const T& a13, const T& a14) { CImg r(1,15); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; @@ -17642,7 +19558,7 @@ namespace cimg_library_suffixed { 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) { + const T& a12, const T& a13, const T& a14, const T& a15) { CImg r(1,16); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; @@ -17668,7 +19584,7 @@ namespace cimg_library_suffixed { \param a3 Fourth matrix value. **/ static CImg matrix(const T& a0, const T& a1, - const T& a2, const T& a3) { + const T& a2, const T& a3) { CImg r(2,2); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; @@ -17688,8 +19604,8 @@ namespace cimg_library_suffixed { \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) { + const T& a3, const T& a4, const T& a5, + const T& a6, const T& a7, const T& a8) { CImg r(3,3); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; @@ -17699,9 +19615,9 @@ namespace cimg_library_suffixed { //! 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) { + 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 r(4,4); T *ptr = r._data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; @@ -17820,8 +19736,8 @@ namespace cimg_library_suffixed { } 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))); + (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))); } //@} @@ -18286,36 +20202,46 @@ namespace cimg_library_suffixed { \param expression C-string describing a math formula, or a list 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 if a formula is allowed or only a list of values. + \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression. + \param list_outputs In case of a mathematical expression, attach a list of images to the specified expression. **/ - CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true) { + 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) { if (is_empty() || !expression || !*expression) return *this; const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); CImg is_error; if (allow_formula) try { // Try to fill values according to a formula. - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"fill"); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) *(ptrd--) = (T)mp(x,y,z,c); - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); - else { + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)?+*this:CImg(), + &base = _base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + "fill",base,this,list_inputs,list_outputs); + T *ptrd = *expression=='<'?end() - 1:_data; + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) *(ptrd--) = (T)mp(x,y,z,c); + else if (*expression=='>') + cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); + else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) + cimg_openmp_if (*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6)) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; #pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); + } } - } - else + else #endif - cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); - } - } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } + cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); + } + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } // If failed, try to recognize a list of values. if (!allow_formula || is_error) { @@ -18326,8 +20252,8 @@ namespace cimg_library_suffixed { T *ptrd = _data; for (double val = 0; *nexpression && nb0 && std::sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { + const int err = cimg_sscanf(nexpression,"%16383[ \n\t0-9.eEinfa+-]%c",item,&sep); + if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { nexpression+=std::strlen(item) + (err>1?1:0); *(ptrd++) = (T)val; } else break; @@ -18348,8 +20274,9 @@ namespace cimg_library_suffixed { } //! Fill sequentially pixel values according to a given expression \newinstance. - CImg get_fill(const char *const values, const bool repeat_values, const bool allow_formula=true) const { - return (+*this).fill(values,repeat_values,allow_formula); + 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 sequentially pixel values according to the values found in another image. @@ -18601,9 +20528,9 @@ namespace cimg_library_suffixed { //! Fill image with random values in specified range. /** - \param val_min Minimal random value. - \param val_max Maximal random value. - \note Random samples are following a uniform distribution. + \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); @@ -18628,7 +20555,7 @@ namespace cimg_library_suffixed { CImg& round(const double y=1, const int rounding_type=0) { if (y>0) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if (size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type); return *this; @@ -18675,7 +20602,7 @@ namespace cimg_library_suffixed { } break; case 1 : { // Uniform noise cimg_rof(*this,ptrd,T) { - Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::crand()); + Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::rand(-1,1)); if (val>vmax) val = vmax; if (val::is_float()?1:cimg::type::max()); } - cimg_rof(*this,ptrd,T) if (cimg::rand()*100=65536) +#pragma omp parallel for cimg_openmp_if (size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a); return *this; @@ -18781,9 +20708,9 @@ namespace cimg_library_suffixed { 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). + \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p>=0). \par Example \code const CImg img("reference.jpg"), res = img.get_norm(); @@ -18792,18 +20719,18 @@ namespace cimg_library_suffixed { \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(); + if (_spectrum==1 && norm_type) return get_abs(); const unsigned long whd = (unsigned long)_width*_height*_depth; CImg res(_width,_height,_depth); switch (norm_type) { - case -1 : { // Linf norm + case -1 : { // Linf-norm. #ifdef cimg_use_openmp #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) #endif @@ -18819,7 +20746,23 @@ namespace cimg_library_suffixed { } } } break; - case 1 : { // L1 norm + case 0 : { // L0-norm. +#ifdef cimg_use_openmp +#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) +#endif + cimg_forYZ(*this,y,z) { + const unsigned long off = (unsigned long)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + unsigned int n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; } + *(ptrd++) = (Tfloat)n; + } + } + } break; + case 1 : { // L1-norm. #ifdef cimg_use_openmp #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) #endif @@ -18835,7 +20778,7 @@ namespace cimg_library_suffixed { } } } break; - default : { // L2 norm + case 2 : { // L2-norm. #ifdef cimg_use_openmp #pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) #endif @@ -18850,6 +20793,22 @@ namespace cimg_library_suffixed { *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); } } + } break; + default : { // Linf-norm. +#ifdef cimg_use_openmp +#pragma omp parallel for collapse(2) if (_width>=512 && _height*_depth>=16) +#endif + cimg_forYZ(*this,y,z) { + const unsigned long off = (unsigned long)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; @@ -18870,7 +20829,7 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; const T a = min_value=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (*ptrdb)?b:*ptrd); return *this; @@ -18903,14 +20862,14 @@ namespace cimg_library_suffixed { if (range>0) { if (keep_range) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*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 #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) { const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); @@ -18942,7 +20901,7 @@ namespace cimg_library_suffixed { if (strict_threshold) { if (soft_threshold) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T v = *ptrd; @@ -18950,13 +20909,13 @@ namespace cimg_library_suffixed { } else #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if (size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0; } else { if (soft_threshold) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if (size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T v = *ptrd; @@ -18964,7 +20923,7 @@ namespace cimg_library_suffixed { } else #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if (size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0; } @@ -19048,7 +21007,7 @@ namespace cimg_library_suffixed { cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } if (!cumul) cumul = 1; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=1048576) +#pragma omp parallel for cimg_openmp_if (size()>=1048576) #endif cimg_rof(*this,ptrd,T) { const int pos = (int)((*ptrd-vmin)*(nb_levels - 1.)/(vmax-vmin)); @@ -19900,7 +21859,7 @@ namespace cimg_library_suffixed { T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { Tfloat - H = cimg::mod((Tfloat)*p1,360.0f), + H = cimg::mod((Tfloat)*p1,(Tfloat)360), S = (Tfloat)*p2, V = (Tfloat)*p3, R = 0, G = 0, B = 0; @@ -19987,7 +21946,7 @@ namespace cimg_library_suffixed { T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { const Tfloat - H = cimg::mod((Tfloat)*p1,360.0f), + H = cimg::mod((Tfloat)*p1,(Tfloat)360), S = (Tfloat)*p2, L = (Tfloat)*p3, q = 2*L<1?L*(1 + S):(L + S - L*S), @@ -20060,7 +22019,7 @@ namespace cimg_library_suffixed { T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { Tfloat - H = cimg::mod((Tfloat)*p1,360.0f), + H = cimg::mod((Tfloat)*p1,(Tfloat)360), S = (Tfloat)*p2, I = (Tfloat)*p3, a = I*(1-S), @@ -20276,12 +22235,12 @@ namespace cimg_library_suffixed { 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 long N = (unsigned long)_width*_height*_depth; N; --N) { Tfloat - C = (Tfloat)*(ps1++), - M = (Tfloat)*(ps2++), - Y = (Tfloat)*(ps3++), - K = cimg::min(C,M,Y); - if (K>=255) C = M = Y = 0; - else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; } + C = (Tfloat)*(ps1++), + M = (Tfloat)*(ps2++), + Y = (Tfloat)*(ps3++), + K = cimg::min(C,M,Y); + if (K>=255) C = M = Y = 0; + else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; } *(pd1++) = (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)); @@ -20307,11 +22266,11 @@ namespace cimg_library_suffixed { Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2); for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) { const Tfloat - C = (Tfloat)*(ps1++), - M = (Tfloat)*(ps2++), - Y = (Tfloat)*(ps3++), - K = (Tfloat)*(ps4++), - K1 = 1 - K/255, + C = (Tfloat)*(ps1++), + M = (Tfloat)*(ps2++), + Y = (Tfloat)*(ps3++), + K = (Tfloat)*(ps4++), + K1 = 1 - K/255, nC = C*K1 + K, nM = M*K1 + K, nY = Y*K1 + K; @@ -20613,8 +22572,8 @@ namespace cimg_library_suffixed { if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this; if (is_empty()) return assign(sx,sy,sz,sc,(T)0); if (interpolation_type==-1 && sx*sy*sz*sc==size()) { - _width = sx; _height = sy; _depth = sz; _spectrum = sc; - return *this; + _width = sx; _height = sy; _depth = sz; _spectrum = sc; + return *this; } return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions, centering_x,centering_y,centering_z,centering_c).move_to(*this); @@ -20661,7 +22620,7 @@ namespace cimg_library_suffixed { cc = (int)(centering_c*((int)sc - spectrum())); switch (boundary_conditions) { - case 2 : { // Periodic borders. + case 2 : { // Periodic boundary. res.assign(sx,sy,sz,sc); const int x0 = ((int)xc%width()) - width(), @@ -20677,7 +22636,7 @@ namespace cimg_library_suffixed { for (int x = x0; x<(int)sx; x+=width()) res.draw_image(x,y,z,c,*this); } break; - case 1 : { // Neumann borders. + case 1 : { // Neumann boundary. res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this); CImg sprite; if (xc>0) { // X-backward @@ -20715,7 +22674,7 @@ namespace cimg_library_suffixed { for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite); } } break; - default : // Dirichlet borders. + default : // Dirichlet boundary. res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this); } break; @@ -20786,17 +22745,17 @@ namespace cimg_library_suffixed { cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); } ++y; unsigned long dy = *(poff_y++); - for (;!dy && y 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 mask[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) @@ -21617,7 +23575,7 @@ namespace cimg_library_suffixed { _n1##x = (int)( \ (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ (I[3] = I[4] = (T)(img)(0,y,z,c)), \ - (I[7] = (T)(img)(0,_n1##y,z,c)), \ + (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)), \ @@ -21671,7 +23629,7 @@ namespace cimg_library_suffixed { _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)), \ + (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)), \ @@ -22047,178 +24005,178 @@ namespace cimg_library_suffixed { const T* ptrs = _data; if (!cimg::strncasecmp(permut,"xyzc",4)) return +*this; if (!cimg::strncasecmp(permut,"xycz",4)) { - res.assign(_width,_height,_spectrum,_depth); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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 : { + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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 long siz = (unsigned long)_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 long siz = (unsigned long)_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 long siz = (unsigned long)_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 long siz = (unsigned long)_width*_height*_depth; siz; --siz) { - *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++); - } - } break; - default : { + 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 long siz = (unsigned long)_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 long siz = (unsigned long)_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 long siz = (unsigned long)_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 long siz = (unsigned long)_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++); - } - } + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + 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); + 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++); + cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); } if (!res) throw CImgArgumentException(_cimg_instance @@ -22384,11 +24342,11 @@ namespace cimg_library_suffixed { } } } break; - default : - throw CImgArgumentException(_cimg_instance + default : + throw CImgArgumentException(_cimg_instance "rotate(): Invalid specified border conditions %d " "(should be { 0=dirichlet | 1=neumann | 2=periodic }).", - cimg_instance, + cimg_instance, boundary); } } @@ -22653,7 +24611,7 @@ namespace cimg_library_suffixed { cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0); } } - } else { // Absolute warp. + } else { // Backward-absolute warp. if (interpolation==2) { // Cubic interpolation. if (boundary_conditions==2) // Periodic boundaries. #ifdef cimg_use_openmp @@ -22831,7 +24789,7 @@ namespace cimg_library_suffixed { cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0); } } - } else { // Absolute warp. + } else { // Backward-absolute warp. if (interpolation==2) { // Cubic interpolation. if (boundary_conditions==2) // Periodic boundaries. #ifdef cimg_use_openmp @@ -22904,7 +24862,7 @@ namespace cimg_library_suffixed { } } - } else if (warp._spectrum==3) { // 3d warping. + } else { // 3d warping. if (mode>=3) { // Forward-relative warp. res.fill(0); if (interpolation>=1) // Linear interpolation. @@ -23033,7 +24991,7 @@ namespace cimg_library_suffixed { cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0); } } - } else { // Absolute warp. + } else { // Backward-absolute warp. if (interpolation==2) { // Cubic interpolation. if (boundary_conditions==2) // Periodic boundaries. #ifdef cimg_use_openmp @@ -23235,21 +25193,21 @@ namespace cimg_library_suffixed { 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; + const int x0 = coords[0], x1 = coords[1]; + if (x0>=0 && x1>=0) crop(x0,x1); + } break; case 'y' : { - const int y0 = coords[0], y1 = coords[1]; - if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1); - } break; + const int y0 = coords[0], y1 = coords[1]; + if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1); + } break; case 'z' : { - const int z0 = coords[0], z1 = coords[1]; - if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1); - } break; + const int z0 = coords[0], z1 = coords[1]; + if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1); + } break; default : { - const int c0 = coords[0], c1 = coords[1]; - if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1); - } + const int c0 = coords[0], c1 = coords[1]; + if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1); + } } } return *this; @@ -23281,32 +25239,32 @@ namespace cimg_library_suffixed { const char axis = cimg::uncase(*s); switch (axis) { case 'x' : { - 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); } - } + 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 (x0==width() && x1==-1) return assign(); else crop(x0,x1); - } break; + } break; case 'y' : { - 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); } - } + 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 (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1); - } break; + } break; default : { - 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 (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1); - } + 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 (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1); + } } } return *this; @@ -23334,41 +25292,41 @@ namespace cimg_library_suffixed { 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) { + if (x0>=0) { for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c) if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); } } - res = CImg::vector(x0,x1); + 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) { + if (y0>=0) { for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c) if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); } } - res = CImg::vector(y0,y1); + 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) { + if (z0>=0) { for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c) if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); } } - res = CImg::vector(z0,z1); + 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) { + if (c0>=0) { for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z) if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; } } - res = CImg::vector(c0,c1); + res = CImg::vector(c0,c1); } } return res; @@ -23747,7 +25705,7 @@ namespace cimg_library_suffixed { _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"); + 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); @@ -23972,7 +25930,7 @@ namespace cimg_library_suffixed { res.assign(_width/dp + (_width%dp?1:0),1,1); const unsigned int pe = _width - dp; #ifdef cimg_use_openmp -#pragma omp parallel for if (res._width>=128 && _height*_depth*_spectrum>=128) +#pragma omp parallel for cimg_openmp_if (res._width>=128 && _height*_depth*_spectrum>=128) #endif for (unsigned int p = 0; p=128 && _width*_depth*_spectrum>=128) +#pragma omp parallel for cimg_openmp_if (res._width>=128 && _width*_depth*_spectrum>=128) #endif for (unsigned int p = 0; p=128 && _width*_height*_spectrum>=128) +#pragma omp parallel for cimg_openmp_if (res._width>=128 && _width*_height*_spectrum>=128) #endif for (unsigned int p = 0; p=128 && _width*_height*_depth>=128) +#pragma omp parallel for cimg_openmp_if (res._width>=128 && _width*_height*_depth>=128) #endif for (unsigned int p = 0; p I; switch (mask._depth) { case 3 : { - T I[27] = { 0 }; + I.assign(27); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24357,8 +26317,9 @@ namespace cimg_library_suffixed { } } break; case 2 : { - T I[8] = { 0 }; + I.assign(8); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24384,8 +26345,9 @@ namespace cimg_library_suffixed { case 1 : switch (mask._width) { case 6 : { - T I[36] = { 0 }; + I.assign(36); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24423,8 +26385,9 @@ namespace cimg_library_suffixed { } } break; case 5 : { - T I[25] = { 0 }; + I.assign(25); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24454,8 +26417,9 @@ namespace cimg_library_suffixed { } } break; case 4 : { - T I[16] = { 0 }; + I.assign(16); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24479,8 +26443,9 @@ namespace cimg_library_suffixed { } } break; case 3 : { - T I[9] = { 0 }; + I.assign(9); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24500,8 +26465,9 @@ namespace cimg_library_suffixed { } } break; case 2 : { - T I[4] = { 0 }; + I.assign(4); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24520,6 +26486,7 @@ namespace cimg_library_suffixed { case 1 : if (is_normalized) res.fill(1); else cimg_forC(res,c) { + cimg_test_abort(); 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]; @@ -24527,15 +26494,16 @@ namespace cimg_library_suffixed { break; } } - } else { // Generic version for other masks and borders conditions. + } else { // Generic version for other masks and boundary conditions. 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; #ifdef cimg_use_openmp -#pragma omp parallel for if (res._spectrum>=2) +#pragma omp parallel for cimg_openmp_if (res._spectrum>=2) #endif cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { // Normalized correlation. @@ -24767,9 +26735,10 @@ namespace cimg_library_suffixed { 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; #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { // Normalized erosion. @@ -25056,9 +27025,10 @@ namespace cimg_library_suffixed { 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; #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { // Normalized dilation. @@ -25372,44 +27342,44 @@ namespace cimg_library_suffixed { // Check labels of the neighbors. bool is_same_label = true; - unsigned int label = 0; + T label = 0; if (x - 1>=0) { if ((*this)(x - 1,y,z)) { - if (!label) label = (unsigned int)(*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(is_queued,sizeQ,priority(x - 1,y,z),x - 1,y,z); } if (x + 1=0) { if ((*this)(x,y - 1,z)) { - if (!label) label = (unsigned int)(*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(is_queued,sizeQ,priority(x,y - 1,z),x,y - 1,z); } if (y + 1=0) { if ((*this)(x,y,z - 1)) { - if (!label) label = (unsigned int)(*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(is_queued,sizeQ,priority(x,y,z - 1),x,y,z - 1); } if (z + 10; --k) val[k] = val[k - 1]; - } - for (int n = pass; n0; --k) val[k] = val[k - 1]; - } - if (!pass) data -= off; - } + const double iplus = (boundary_conditions?data[(N - 1)*off]: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 border condition */ + 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:0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - /* apply Triggs border condition */ - 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; - } + 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:0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + /* apply Triggs border condition */ + 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:0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - /* apply Triggs border condition */ - 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; - } + 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:0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + /* apply Triggs border condition */ + 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:0); - for (int k = 0; k<4; ++k) val[k] = 0; - } else { - /* apply Triggs border condition */ - 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; - } + 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:0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + /* apply Triggs border condition */ + 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; } } @@ -25801,18 +27771,18 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; const char naxis = cimg::uncase(axis); const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; - if (is_empty() || (nsigma<0.1f && !order)) return *this; + if (is_empty() || (nsigma<0.5f && !order)) return *this; const double - nnsigma = nsigma<0.1f?0.1f: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; + 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) { @@ -25820,31 +27790,31 @@ namespace cimg_library_suffixed { #ifdef cimg_use_openmp #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) #endif - cimg_forYZC(*this,y,z,c) - _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); + cimg_forYZC(*this,y,z,c) + _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); } break; case 'y' : { #ifdef cimg_use_openmp #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) #endif - cimg_forXZC(*this,x,z,c) - _cimg_recursive_apply(data(x,0,z,c),filter,_height,(unsigned long)_width,order,boundary_conditions); + cimg_forXZC(*this,x,z,c) + _cimg_recursive_apply(data(x,0,z,c),filter,_height,(unsigned long)_width,order,boundary_conditions); } break; case 'z' : { #ifdef cimg_use_openmp #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) #endif - cimg_forXYC(*this,x,y,c) - _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(unsigned long)(_width*_height), - order,boundary_conditions); + cimg_forXYC(*this,x,y,c) + _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(unsigned long)(_width*_height), + order,boundary_conditions); } break; default : { #ifdef cimg_use_openmp #pragma omp parallel for collapse(3) if (_width>=256 && _height*_depth*_spectrum>=16) #endif - cimg_forXYZ(*this,x,y,z) - _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(unsigned long)(_width*_height*_depth), - order,boundary_conditions); + cimg_forXYZ(*this,x,y,z) + _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(unsigned long)(_width*_height*_depth), + order,boundary_conditions); } } return *this; @@ -25939,6 +27909,7 @@ namespace cimg_library_suffixed { Tfloat *ptrd = velocity._data, veloc_max = 0; if (is_3d) // 3d version cimg_forC(*this,c) { + cimg_test_abort(); CImg_3x3x3(I,Tfloat); cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { const Tfloat @@ -25956,6 +27927,7 @@ namespace cimg_library_suffixed { } else // 2d version cimg_forZC(*this,z,c) { + cimg_test_abort(); CImg_3x3(I,Tfloat); cimg_for3x3(*this,x,y,z,c,I,Tfloat) { const Tfloat @@ -26003,6 +27975,7 @@ namespace cimg_library_suffixed { *(pd3++) = (Tfloat)n; } + cimg_test_abort(); #ifdef cimg_use_openmp #pragma omp parallel for collapse(2) if (_width>=256 && _height*_depth>=2) firstprivate(val) #endif @@ -26096,8 +28069,9 @@ namespace cimg_library_suffixed { *(pd2++) = (Tfloat)n; } + cimg_test_abort(); #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=256 && _height>=2) firstprivate(val) +#pragma omp parallel for cimg_openmp_if (_width>=256 && _height>=2) firstprivate(val) #endif cimg_forXY(*this,x,y) { val.fill(0); @@ -26775,7 +28749,7 @@ namespace cimg_library_suffixed { const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; if (is_fast_approx) #ifdef cimg_use_openmp -#pragma omp parallel for if (res._width>=32 && res._height>=4) firstprivate(P,Q) +#pragma omp parallel for cimg_openmp_if (res._width>=32 && res._height>=4) firstprivate(P,Q) #endif cimg_forXY(res,x,y) { // 2d fast approximation. P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); @@ -26794,7 +28768,7 @@ namespace cimg_library_suffixed { else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); } else #ifdef cimg_use_openmp -#pragma omp parallel for if (res._width>=32 && res._height>=4) firstprivate(P,Q) +#pragma omp parallel for cimg_openmp_if (res._width>=32 && res._height>=4) firstprivate(P,Q) #endif cimg_forXY(res,x,y) { // 2d exact algorithm. P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); @@ -26888,10 +28862,10 @@ namespace cimg_library_suffixed { else switch (n) { // Without threshold. case 3 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { - T I[9] = { 0 }; + T I[9]; CImg_3x3(J,T); cimg_for3x3(*this,x,y,0,c,I,T) { std::memcpy(J,I,9*sizeof(T)); @@ -26908,10 +28882,10 @@ namespace cimg_library_suffixed { } break; case 5 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { - T I[25] = { 0 }; + T I[25]; CImg_5x5(J,T); cimg_for5x5(*this,x,y,0,c,I,T) { std::memcpy(J,I,25*sizeof(T)); @@ -26967,9 +28941,10 @@ namespace cimg_library_suffixed { } } else { // 1d + CImg I; if (threshold>0) #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=16 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width>=16 && _spectrum>=2) #endif cimg_forXC(*this,x,c) { // With threshold. const int @@ -26986,26 +28961,26 @@ namespace cimg_library_suffixed { else switch (n) { // Without threshold. case 2 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { - T I[4] = { 0 }; + 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 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { - T I[9] = { 0 }; + I.assign(9); cimg_for3x3(*this,x,y,0,c,I,T) res(x,c) = I[3]=16 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width>=16 && _spectrum>=2) #endif cimg_forXC(*this,x,c) { const int @@ -27058,7 +29033,7 @@ namespace cimg_library_suffixed { } } #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=512 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=512 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; @@ -27105,7 +29080,7 @@ namespace cimg_library_suffixed { CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); if (sigma>0) G.blur(sigma); #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=32 && _height>=16) +#pragma omp parallel for cimg_openmp_if (_width>=32 && _height>=16) #endif cimg_forY(G,y) { CImg val, vec; @@ -27120,7 +29095,7 @@ namespace cimg_library_suffixed { } } #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=512 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height>=512 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; @@ -27202,7 +29177,7 @@ namespace cimg_library_suffixed { switch (scheme) { // 3d. case -1 : { // Backward finite differences. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27217,7 +29192,7 @@ namespace cimg_library_suffixed { } break; case 1 : { // Forward finite differences. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27242,7 +29217,7 @@ namespace cimg_library_suffixed { } break; default : { // Central finite differences. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27370,7 +29345,7 @@ namespace cimg_library_suffixed { if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27431,7 +29406,7 @@ namespace cimg_library_suffixed { else if (axis1=='x' && axis2=='z') { // Ixz valid_axis = true; #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res[l2].data(0,0,0,c); @@ -27453,7 +29428,7 @@ namespace cimg_library_suffixed { else if (axis1=='y' && axis2=='z') { // Iyz valid_axis = true; #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res[l2].data(0,0,0,c); @@ -27464,7 +29439,7 @@ namespace cimg_library_suffixed { else if (axis1=='z' && axis2=='z') { // Izz valid_axis = true; #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res[l2].data(0,0,0,c); @@ -27492,7 +29467,7 @@ namespace cimg_library_suffixed { CImg res(_width,_height,_depth,_spectrum); if (_depth>1) { // 3d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res.data(0,0,0,c); @@ -27501,7 +29476,7 @@ namespace cimg_library_suffixed { } } else if (_height>1) { // 2d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height>=1048576 && _depth*_spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res.data(0,0,0,c); @@ -27510,7 +29485,7 @@ namespace cimg_library_suffixed { } } else { // 1d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=1048576 && _height*_depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width>=1048576 && _height*_depth*_spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res.data(0,0,0,c); @@ -27538,7 +29513,7 @@ namespace cimg_library_suffixed { switch (scheme) { case 0 : { // classical central finite differences #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat @@ -27561,7 +29536,7 @@ namespace cimg_library_suffixed { } break; case 1 : { // Forward/backward finite differences (version 1). #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat @@ -27584,7 +29559,7 @@ namespace cimg_library_suffixed { } break; default : { // Forward/backward finite differences (version 2). #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat @@ -27611,7 +29586,7 @@ namespace cimg_library_suffixed { switch (scheme) { case 0 : { // classical central finite differences #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height>=1048576 && _depth*_spectrum>=2) #endif 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); @@ -27628,7 +29603,7 @@ namespace cimg_library_suffixed { } break; case 1 : { // Forward/backward finite differences (version 1). #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height>=1048576 && _depth*_spectrum>=2) #endif 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); @@ -27645,7 +29620,7 @@ namespace cimg_library_suffixed { } break; default : { // Forward/backward finite differences (version 2). #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_width*_height>=1048576 && _depth*_spectrum>=2) #endif 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); @@ -27750,14 +29725,15 @@ namespace cimg_library_suffixed { \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 constraints A list of constrained pixels (as a Nx4 or Nx6 image), i.e defining N points - of the estimated flow having a known value. + \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, - const CImg& constraints=CImg::empty()) { - return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,constraints). + const CImg& guide=CImg::const_empty()) { + return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). move_to(*this); } @@ -27766,7 +29742,7 @@ namespace cimg_library_suffixed { 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 CImg& constraints=CImg::empty()) const { + const CImg& guide=CImg::const_empty()) const { if (is_empty() || !source) return +*this; if (!is_sameXYZC(source)) throw CImgArgumentException(_cimg_instance @@ -27780,7 +29756,18 @@ namespace cimg_library_suffixed { "(should be >=0)", cimg_instance, precision); + const bool is_3d = source._depth>1; + const unsigned int constraint = is_3d?3:2; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum0?nb_scales: @@ -27790,14 +29777,8 @@ namespace cimg_library_suffixed { 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); - if (constraints && (constraints.height()!=4 || is_3d) && (constraints.height()!=6 || !is_3d)) - throw CImgArgumentException(_cimg_instance - "displacement(): Invalid specified constraints image (%u,%u,%u,%u,%p) " - " (should be a Nx4 or Nx6 image).", - cimg_instance, - constraints._width,constraints._height,constraints._depth,constraints._spectrum, - constraints._data); - CImg U; + 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 @@ -27808,36 +29789,21 @@ namespace cimg_library_suffixed { 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); - if (constraints) { - if (is_3d) cimg_forX(constraints,k) { - const int - cx = (int)(constraints(k,0)*U.width()/width()), - cy = (int)(constraints(k,1)*U.height()/height()), - cz = (int)(constraints(k,2)*U.depth()/depth()); - if (U.contains(U(cx,cy,cz))) { - U(cx,cy,cz,0) = (float)(constraints(k,3)/factor); - U(cx,cy,cz,1) = (float)(constraints(k,4)/factor); - U(cx,cy,cz,2) = (float)(constraints(k,5)/factor); - } - } - else cimg_forX(constraints,k) { - const int - cx = (int)(constraints(k,0)*U.width()/width()), - cy = (int)(constraints(k,1)*U.height()/height()); - if (U.contains(U(cx,cy))) { - U(cx,cy,0) = (float)(constraints(k,2)/factor); - U(cx,cy,1) = (float)(constraints(k,3)/factor); - } - } + 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) // Isotropic regularization. #ifdef cimg_use_openmp @@ -27867,8 +29833,28 @@ namespace cimg_library_suffixed { smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt); _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz; } + if (is_backward) { // Constraint displacement vectors to stay in image. + if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } _energy+=delta_I*delta_I + smoothness*_energy_regul; } + if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints. + U(x,y,z,0) = V(x,y,z,0)/factor; + U(x,y,z,1) = V(x,y,z,1)/factor; + U(x,y,z,2) = V(x,y,z,2)/factor; + } } else { // Anisotropic regularization. const float nsmoothness = -smoothness; #ifdef cimg_use_openmp @@ -27913,25 +29899,34 @@ namespace cimg_library_suffixed { )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt); _energy_regul+=N; } + if (is_backward) { // Constraint displacement vectors to stay in image. + if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } _energy+=delta_I*delta_I + nsmoothness*_energy_regul; } + if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints. + U(x,y,z,0) = V(x,y,z,0)/factor; + U(x,y,z,1) = V(x,y,z,1)/factor; + U(x,y,z,2) = V(x,y,z,2)/factor; + } } } - if (constraints) cimg_forX(constraints,k) { - const int - cx = (int)(constraints(k,0)*U.width()/width()), - cy = (int)(constraints(k,1)*U.height()/height()), - cz = (int)(constraints(k,2)*U.depth()/depth()); - if (U.contains(U(cx,cy,cz))) { - U(cx,cy,cz,0) = (float)(constraints(k,3)/factor); - U(cx,cy,cz,1) = (float)(constraints(k,4)/factor); - U(cx,cy,cz,2) = (float)(constraints(k,5)/factor); - } - } } else { // 2d version. if (smoothness>=0) // Isotropic regularization. #ifdef cimg_use_openmp -#pragma omp parallel for if (_height>=8 && _width>=16) reduction(+:_energy) +#pragma omp parallel for cimg_openmp_if (_height>=8 && _width>=16) reduction(+:_energy) #endif 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; } + 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; #ifdef cimg_use_openmp -#pragma omp parallel for if (_height>=8 && _width>=16) reduction(+:_energy) +#pragma omp parallel for cimg_openmp_if (_height>=8 && _width>=16) reduction(+:_energy) #endif 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; } + 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; + } } } - if (constraints) cimg_forX(constraints,k) { - const int - cx = (int)(constraints(k,0)*U.width()/width()), - cy = (int)(constraints(k,1)*U.height()/height()); - if (U.contains(U(cx,cy))) { - U(cx,cy,0) = (float)(constraints(k,2)/factor); - U(cx,cy,1) = (float)(constraints(k,3)/factor); - } - } } const float d_energy = (_energy - energy)/(sw*sh*sd); if (d_energy<=0 && -d_energy<_precision) break; @@ -28009,6 +30025,481 @@ namespace cimg_library_suffixed { return U; } + //! 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 _get_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 _get_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 _get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms, + CImg::const_empty(), + false,CImg::empty()); + } + + 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 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 unsigned long + offx1 = (unsigned long)img1._width - psizew, + offx2 = (unsigned long)img2._width - psizew, + offy1 = (unsigned long)img1._width*img1._height - psizeh*img1._width, + offy2 = (unsigned long)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 unsigned long + offx1 = (unsigned long)img1._width - psizew, + offx2 = (unsigned long)img2._width - psizew, + offy1 = (unsigned long)img1._width*img1._height - psizeh*img1._width - psizew, + offy2 = (unsigned long)img2._width*img2._height - psizeh*img2._width - psizew, + offz1 = (unsigned long)img1._width*img1._height*img1._depth - psized*img1._width*img1._height - + psizeh*img1._width - psizew, + offz2 = (unsigned long)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. @@ -28093,7 +30584,7 @@ namespace cimg_library_suffixed { const unsigned long wh = (unsigned long)_width*_height; #if defined(cimg_use_openmp) && !cimg_is_gcc49x -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { CImg g(_width), dt(_width), s(_width), t(_width); @@ -28146,7 +30637,7 @@ namespace cimg_library_suffixed { if (!is_value) return fill(cimg::type::max()); const unsigned long wh = (unsigned long)_width*_height; #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) #endif cimg_forC(*this,c) { CImg img = get_shared_channel(c); @@ -28196,7 +30687,7 @@ namespace cimg_library_suffixed { return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); } - //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. + //! 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, @@ -28400,7 +30891,7 @@ namespace cimg_library_suffixed { CImg state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen. #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) firstprivate(Q,state) +#pragma omp parallel for cimg_openmp_if (_spectrum>=2) firstprivate(Q,state) #endif cimg_forC(*this,c) { const CImg img = get_shared_channel(c); @@ -29350,7 +31841,7 @@ namespace cimg_library_suffixed { **/ 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 @@ -30256,7 +32747,9 @@ namespace cimg_library_suffixed { 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(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } ~_functor2d_expr() { delete mp; } float operator()(const float x, const float y) const { return (float)(*mp)(x,y,0,0); @@ -30282,7 +32775,9 @@ namespace cimg_library_suffixed { 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(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)(x,y,z,0); } @@ -30347,12 +32842,12 @@ namespace cimg_library_suffixed { 0.,0.,size_z, 0.,0.,0.); for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) { - const float a = (float)(angle*cimg::PI/180); + const float a = (float)(angle*cimg::PI/180); CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices); } const unsigned int nbr = vertices._width - 2; for (unsigned int p = 0; p::vector(1,next,curr).move_to(primitives); CImg::vector(0,curr,next).move_to(primitives); } @@ -30384,13 +32879,13 @@ namespace cimg_library_suffixed { 0.,0.,0., 0.,0.,size_z); for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) { - const float a = (float)(angle*cimg::PI/180); + const float a = (float)(angle*cimg::PI/180); CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices); CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices); } const unsigned int nbr = (vertices._width - 2)/2; for (unsigned int p = 0; p::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); @@ -30423,21 +32918,21 @@ namespace cimg_library_suffixed { if (!subdivisions1 || !subdivisions2) return CImg(); CImgList vertices; for (unsigned int v = 0; v::vector(x,y,z).move_to(vertices); } } for (unsigned int vv = 0; vv::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives); @@ -30476,7 +32971,7 @@ namespace cimg_library_suffixed { CImg::vector(fx*x,fy*y,0).move_to(vertices); for (unsigned int y = 0; y::vector(off1,off4,off3,off2).move_to(primitives); + CImg::vector(off1,off4,off3,off2).move_to(primitives); } return vertices>'x'; } @@ -30514,8 +33009,8 @@ namespace cimg_library_suffixed { // Recurse subdivisions for (unsigned int i = 0; i>1); const bool xdir = x0=width()) return *this; if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; } @@ -32218,7 +34713,7 @@ namespace cimg_library_suffixed { xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ - _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ + _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) @@ -34350,17 +36845,17 @@ namespace cimg_library_suffixed { const tc *const color, const float opacity, const unsigned int pattern) { return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true). - draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false). - draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false). - draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false). - draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true). - draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false). - draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false). - draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false). - draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true). - draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true). - draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true). - draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); + draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false). + draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false). + draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false). + draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true). + draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false). + draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false). + draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false). + draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true). + draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true). + draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true). + draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); } //! Draw a filled 2d rectangle. @@ -34928,7 +37423,7 @@ namespace cimg_library_suffixed { 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 (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 " @@ -34957,7 +37452,7 @@ namespace cimg_library_suffixed { offZ = (unsigned long)_width*_height*(_depth - lZ), soffZ = (unsigned long)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); + T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0); for (int c = 0; c txt(32); CImg label; if (siz<=0) { // Degenerated case. draw_line(0,y,_width - 1,y,color,opacity,pattern); if (!siz) { - cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_x); + 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]!='0' || txt[1]!=0) + 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]!='0' || txt[1]!=0) + if (allow_zero || *txt!='0' || txt[1]!=0) draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); } } @@ -35322,12 +37817,12 @@ namespace cimg_library_suffixed { const bool allow_zero=true) { if (is_empty()) return *this; int siz = (int)values_y.size() - 1; - char txt[32] = { 0 }; + 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,sizeof(txt),"%g",(double)*values_y); + 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, @@ -35335,14 +37830,14 @@ namespace cimg_library_suffixed { _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]!='0' || txt[1]!=0) + 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]=0?_xt:x + 3; draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity); - if (allow_zero || txt[0]!='0' || txt[1]!=0) + if (allow_zero || *txt!='0' || txt[1]!=0) draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); } } @@ -35541,18 +38036,20 @@ namespace cimg_library_suffixed { switch (plot_type%4) { case 1 : { // Segments int oX = 0, oY = (int)((data[0] - m)/ca); - const float fx = (float)_width1/siz1; if (siz==1) { const int Y = (int)((*data - m)/ca); draw_line(0,Y,width() - 1,Y,color,opacity,pattern); - } else for (unsigned long off = 1; off ndata(data._data,siz,1,1,1,true); @@ -35566,11 +38063,11 @@ namespace cimg_library_suffixed { } break; case 3 : { // Bars const int Y0 = (int)(-m/ca); - const float fx = (float)_width/(siz - 1); + const float fx = (float)_width/siz1; int oX = 0; cimg_foroff(data,off) { const int - X = (int)((off + 1)*fx), + 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). @@ -35889,7 +38386,8 @@ namespace cimg_library_suffixed { for (int y0 = 0; y0M?M:val); } @@ -35898,21 +38396,24 @@ namespace cimg_library_suffixed { for (int x0=0; x0M?M:val); } for (int y0 = 0; y0M?M:val); } for (int y = -delta2; yM?M:val); } } @@ -36295,7 +38796,7 @@ namespace cimg_library_suffixed { const bool is_double_sided=false, const float focale=700, const float lightx=0, const float lighty=0, const float lightz=-5e8, const float specular_lightness=0.2f, const float specular_shininess=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + 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()); } @@ -36310,7 +38811,7 @@ namespace cimg_library_suffixed { const float lightx, const float lighty, const float lightz, const float specular_lightness, const float specular_shininess, CImg& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + 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); } @@ -36325,7 +38826,7 @@ namespace cimg_library_suffixed { const bool is_double_sided=false, const float focale=700, const float lightx=0, const float lighty=0, const float lightz=-5e8, const float specular_lightness=0.2f, const float specular_shininess=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + 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()); } @@ -36340,7 +38841,7 @@ namespace cimg_library_suffixed { const float lightx, const float lighty, const float lightz, const float specular_lightness, const float specular_shininess, CImg& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + 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); } @@ -36383,17 +38884,18 @@ namespace cimg_library_suffixed { 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; 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.data()); - if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety. - #ifndef cimg_use_board if (pboard) return *this; #endif + if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety. + const float nspec = 1 - (specular_lightness<0.0f?0.0f:(specular_lightness>1.0f?1.0f:specular_lightness)), nspec2 = 1 + (specular_shininess<0.0f?0.0f:specular_shininess), @@ -36457,7 +38959,7 @@ namespace cimg_library_suffixed { const float absfocale = focale?cimg::abs(focale):0; if (absfocale) { #ifdef cimg_use_openmp -#pragma omp parallel for if (projections.size()>4096) +#pragma omp parallel for cimg_openmp_if (projections.size()>4096) #endif cimg_forX(projections,l) { // Perspective projection const tpfloat @@ -36471,7 +38973,7 @@ namespace cimg_library_suffixed { } else { #ifdef cimg_use_openmp -#pragma omp parallel for if (projections.size()>4096) +#pragma omp parallel for cimg_openmp_if (projections.size()>4096) #endif cimg_forX(projections,l) { // Parallel projection const tpfloat @@ -36491,16 +38993,18 @@ namespace cimg_library_suffixed { CImg visibles(primitives._width,1,1,1,~0U); CImg zrange(primitives._width); const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type::min(); - bool is_forward = true; + bool is_forward = zbuffer?true:false; #ifdef cimg_use_openmp -#pragma omp parallel for if (primitives.size()>4096) +#pragma omp parallel for cimg_openmp_if (primitives.size()>4096) #endif cimglist_for(primitives,l) { const CImg& primitive = primitives[l]; switch (primitive.size()) { case 1 : { // Point - if (l<=colors.width() && colors[l].size()!=_spectrum) is_forward = false; + 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) { @@ -36606,16 +39110,21 @@ namespace cimg_library_suffixed { } } break; default : + if (render_type==5) cimg::mutex(10,0); throw CImgArgumentException(_cimg_instance "draw_object3d(): Invalid primitive[%u] with size %u " "(should have size 1,2,3,4,5,6,9 or 12).", cimg_instance, l,primitive.size()); } - // Force transparent primitives to be drawn last when zbuffer is activated. - if (zbuffer && ___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l); } + // 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; @@ -36625,9 +39134,12 @@ namespace cimg_library_suffixed { ++ptrz; } const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data); - if (!nb_visibles) return *this; + 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,zbuffer && is_forward?true:false); + CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward); // Compute light properties CImg lightprops; @@ -36635,7 +39147,7 @@ namespace cimg_library_suffixed { case 3 : { // Flat Shading lightprops.assign(nb_visibles); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if (nb_visibles>4096) #endif cimg_forX(lightprops,l) { const CImg& primitive = primitives(visibles(permutations(l))); @@ -36669,7 +39181,7 @@ namespace cimg_library_suffixed { case 5 : { // Phong-Shading CImg vertices_normals(vertices._width,6,1,1,0); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if (nb_visibles>4096) #endif for (unsigned int l = 0; l& primitive = primitives[visibles(l)]; @@ -36722,7 +39234,7 @@ namespace cimg_library_suffixed { if (render_type==4) { lightprops.assign(vertices._width); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if (nb_visibles>4096) #endif cimg_forX(lightprops,l) { const tpfloat @@ -36743,7 +39255,7 @@ namespace cimg_library_suffixed { lh2 = light_texture._height/2 - 1; lightprops.assign(vertices._width,2); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if (nb_visibles>4096) #endif cimg_forX(lightprops,l) { const tpfloat @@ -36762,7 +39274,6 @@ namespace cimg_library_suffixed { // Draw visible primitives const CImg default_color(1,_spectrum,1,1,(tc)200); - typedef typename to::value_type _to; CImg<_to> _opacity; for (unsigned int l = 0; l& 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 \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 \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,false); + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + return _get_select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false); } //! 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,false); + return _get_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 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); @@ -37585,12 +40107,18 @@ namespace cimg_library_suffixed { 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()) { + const double ratio = cimg::min((double)disp.screen_width()/width(),(double)disp.screen_height()/height()); + get_resize(cimg::max(1,(int)(ratio*width())),cimg::max(1,(int)(ratio*height())),-100,-100).move_to(thumb); + } + const unsigned int old_normalization = disp.normalization(); bool old_is_resized = disp.is_resized(); disp._normalization = 0; disp.show().set_key(0).set_wheel().show_mouse(); - 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 - 1)/2)%_width), @@ -37611,7 +40139,7 @@ namespace cimg_library_suffixed { CImgList primitives3d, sel_primitives3d; CImgList colors3d, sel_colors3d; CImg visu, visu0, view3d; - CImg text(1024); + CImg text(1024); *text = 0; while (!key && !disp.is_closed() && !shape_selected) { @@ -37631,6 +40159,8 @@ namespace cimg_library_suffixed { if (mX>=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 : @@ -37668,35 +40198,33 @@ namespace cimg_library_suffixed { } 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++); + 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). + (+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; - 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); } while (file); (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp); save(filename); - (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename). + (+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; @@ -37729,7 +40257,7 @@ namespace cimg_library_suffixed { X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; } } - if (disp.button()&4) { // Reset positions. + if (disp.button()&4) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = clicked_area = starting_area = 0; visu0.assign(); } @@ -37832,7 +40360,9 @@ namespace cimg_library_suffixed { if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { if (!visu0) { // Create image of projected planes. - __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0).resize(disp); + 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(); } @@ -38095,6 +40625,10 @@ namespace cimg_library_suffixed { } 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. @@ -38106,15 +40640,15 @@ namespace cimg_library_suffixed { if (Y0>Y1) cimg::swap(Y0,Y1); if (Z0>Z1) cimg::swap(Z0,Z1); } - if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; - switch (feature_type) { - case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; + if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; + switch (feature_type) { + case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; case 3 : res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0); // keep no break here! - default : res[0] = X0; res[1] = Y0; res[2] = Z0; - } + 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; @@ -38131,7 +40665,8 @@ namespace cimg_library_suffixed { if (_depth>1) crop.get_projections2d(x,y,z).move_to(img2d); else CImg(crop,false).move_to(img2d); - if (cimg::type::is_float()) { // Check for inf and nan values. + // 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; } @@ -38180,7 +40715,8 @@ namespace cimg_library_suffixed { 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.", @@ -38196,8 +40732,8 @@ namespace cimg_library_suffixed { 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 colormap; if (odimv!=_spectrum) { @@ -38250,8 +40786,8 @@ namespace cimg_library_suffixed { 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); + 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) { @@ -38262,8 +40798,8 @@ namespace cimg_library_suffixed { 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); @@ -38303,27 +40839,27 @@ namespace cimg_library_suffixed { (double)(*this)(x,0,0,_spectrum - 1)); else { cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx); - cimg_forC(*this,c) std::sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); - std::sprintf(message._data + std::strlen(message),")"); + 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 + if (x0>=0 && x1>=0) { + const unsigned int nx0 = (unsigned int)(x0<=x1?x0:x1), nx1 = (unsigned int)(x0<=x1?x1:x0), ny0 = (unsigned int)(y0<=y1?y0:y1), ny1 = (unsigned int)(y0<=y1?y1:y0); - const double + const double cx0 = nxmin + nx0*(nxmax - nxmin)/cimg::max(1U,siz - 1), cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/cimg::max(1U,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._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", + if (y0>=0 && y1>=0) + cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", x0,cx0,cy0,x1 + one,cx1,cy1); - else - std::sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", + else + cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", x0,cx0,x1 + one,cx1); - } + } text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3); visu.draw_image((visu.width() - text.width())/2,1,~text); } @@ -38331,6 +40867,7 @@ namespace cimg_library_suffixed { } // Test keys. + CImg filename(32); switch (okey = key) { #if cimg_OS!=2 case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : @@ -38362,15 +40899,14 @@ namespace cimg_library_suffixed { 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++); + 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).display(disp); + (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename._data).display(disp); } disp.set_key(key,false); okey = 0; } break; @@ -38378,19 +40914,18 @@ namespace cimg_library_suffixed { 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); } 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; @@ -38405,10 +40940,10 @@ namespace cimg_library_suffixed { cx = mx<0?0:(mx>=(int)(siz - one)?(int)(siz - 1 - one):mx), my = mouse_y - 16, cy = my<=0?0:(my>=(disp.height() - 32)?(disp.height() - 32):my); - if (button&1) { + if (button&1) { if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; } } - else if (button&2) { + else if (button&2) { if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; } } else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; } @@ -38417,6 +40952,11 @@ namespace cimg_library_suffixed { } 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; @@ -38566,7 +41106,7 @@ namespace cimg_library_suffixed { } try { - const char *const f_type = cimg::file_type(file,filename); + const char *const f_type = cimg::ftype(file,filename); std::fclose(file); if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename); else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename); @@ -38634,7 +41174,7 @@ namespace cimg_library_suffixed { CImg line(256); *line = 0; int err = std::fscanf(nfile,"%255[^\n]",line._data); unsigned int dx = 0, dy = 1, dz = 1, dc = 1; - std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); + 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); @@ -38702,7 +41242,7 @@ namespace cimg_library_suffixed { 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._data,&c) || c=='\n') { + if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') { dx = cimg::max(cdx,dx); if (++dy>=_height) resize(_width,3*_height/2,1,1,0); cdx = 0; @@ -38751,8 +41291,8 @@ namespace cimg_library_suffixed { 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 @@ -38777,7 +41317,7 @@ namespace cimg_library_suffixed { file_size = (int)std::ftell(nfile); std::fseek(nfile,54,SEEK_SET); } - if (header_size>40) std::fseek(nfile, header_size - 40, SEEK_CUR); + if (header_size>40) std::fseek(nfile,header_size - 40,SEEK_CUR); const int cimg_iobuffer = 24*1024*1024, @@ -39339,7 +41879,7 @@ namespace cimg_library_suffixed { int err, rval, gval, bval; const long cimg_iobuffer = 24*1024*1024; while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item," P%u",&ppm_type)!=1) { + 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'.", @@ -39347,7 +41887,7 @@ namespace cimg_library_suffixed { filename?filename:"(FILE*)"); } while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if ((err=std::sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) { + 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'.", @@ -39357,7 +41897,7 @@ namespace cimg_library_suffixed { if (ppm_type!=1 && ppm_type!=4) { if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) { while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item,"%u",&colormax)!=1) + if (cimg_sscanf(item,"%u",&colormax)!=1) cimg::warn(_cimg_instance "load_pnm(): COLORMAX field is undefined in file '%s'.", cimg_instance, @@ -39423,7 +41963,7 @@ namespace cimg_library_suffixed { for (long to_read = (long)size(); to_read>0; ) { raw.assign(cimg::min(to_read,cimg_iobuffer/2)); cimg::fread(raw._data,raw._width,nfile); - if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + 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++); @@ -39541,7 +42081,7 @@ namespace cimg_library_suffixed { int W = 0, H = 0, err = 0; double scale = 0; while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item," P%c",&pfm_type)!=1) { + 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'.", @@ -39549,7 +42089,7 @@ namespace cimg_library_suffixed { filename?filename:"(FILE*)"); } while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if ((err=std::sscanf(item," %d %d",&W,&H))<2) { + 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'.", @@ -39558,7 +42098,7 @@ namespace cimg_library_suffixed { } if (err==2) { while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); - if (std::sscanf(item,"%lf",&scale)!=1) + if (cimg_sscanf(item,"%lf",&scale)!=1) cimg::warn(_cimg_instance "load_pfm(): SCALE field is undefined in file '%s'.", cimg_instance, @@ -39725,8 +42265,8 @@ namespace cimg_library_suffixed { 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 first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, float *const voxel_size=0, CImg *const description=0) { if (!filename) @@ -39735,8 +42275,8 @@ namespace cimg_library_suffixed { cimg_instance); const unsigned int - nfirst_frame = first_frame 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, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + 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); @@ -39909,11 +42449,11 @@ namespace cimg_library_suffixed { 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 && std::sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format. + 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 && std::sscanf(s_desc,"spacing=%f",&vz)==1) { // fiji format. + if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // fiji format. voxel_size[2] = vz; } } @@ -39927,10 +42467,13 @@ namespace cimg_library_suffixed { if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) CImg::string(s_description).move_to(*description); } - const unsigned int spectrum = is_spp?samplesperpixel:photo==3?3:1; + const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel; assign(nx,ny,1,spectrum); - if (photo>=3 && sampleformat==1 && bitspersample==8 && (samplesperpixel==3 || samplesperpixel==4)) { + 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) { @@ -39942,24 +42485,25 @@ namespace cimg_library_suffixed { } TIFFReadRGBAImage(tif,nx,ny,raster,0); switch (spectrum) { - case 1 : { - cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny - 1 - y) + x] + 128)/257); - } break; - case 3 : { + 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 : { + 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; + break; } _TIFFfree(raster); } else { // Other cases. @@ -40116,11 +42660,11 @@ namespace cimg_library_suffixed { 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._data + 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._data + 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. @@ -40345,8 +42889,8 @@ namespace cimg_library_suffixed { } static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { - CImg item(1024); *item = 0; - char tmp1[64] = { 0 }, tmp2[64] = { 0 }; + 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) @@ -40354,29 +42898,29 @@ namespace cimg_library_suffixed { pixel_type()); while (std::fscanf(file," %63[^\n]%*c",item._data)!=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); + 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) { - std::sscanf(item," VX%*[^0-9.+-]%f",voxel_size); - std::sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1); - std::sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2); + 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.", pixel_type(), - tmp2); + tmp2._data); } } if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) @@ -40530,8 +43074,8 @@ namespace cimg_library_suffixed { 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 @@ -40540,10 +43084,11 @@ namespace cimg_library_suffixed { 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; @@ -40675,19 +43220,16 @@ namespace cimg_library_suffixed { 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]; @@ -40768,9 +43310,9 @@ namespace cimg_library_suffixed { } CImg& _load_raw(std::FILE *const file, const char *const filename, - const unsigned int size_x, const unsigned int size_y, - const unsigned int size_z, const unsigned int size_c, - const bool is_multiplexed, const bool invert_endianness, + const 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 unsigned long offset) { if (!file && !filename) throw CImgArgumentException(_cimg_instance @@ -40841,7 +43383,7 @@ namespace cimg_library_suffixed { CImg& load_yuv(std::FILE *const file, 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 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); } @@ -40849,7 +43391,7 @@ namespace cimg_library_suffixed { static CImg get_load_yuv(std::FILE *const file, 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 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); } @@ -40905,7 +43447,7 @@ namespace cimg_library_suffixed { filename?filename:"(FILE*)"); } do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); - if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { + 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'.", @@ -40918,7 +43460,7 @@ namespace cimg_library_suffixed { float X = 0, Y = 0, Z = 0; cimg_forX(*this,l) { do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); - if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { + 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'.", @@ -40949,7 +43491,7 @@ namespace cimg_library_suffixed { 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); } @@ -40963,7 +43505,7 @@ namespace cimg_library_suffixed { 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); } @@ -40977,7 +43519,7 @@ namespace cimg_library_suffixed { 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); } @@ -40991,7 +43533,7 @@ namespace cimg_library_suffixed { 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); } @@ -41005,7 +43547,7 @@ namespace cimg_library_suffixed { 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))); @@ -41021,7 +43563,7 @@ namespace cimg_library_suffixed { 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))); @@ -41037,7 +43579,7 @@ namespace cimg_library_suffixed { 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); @@ -41054,7 +43596,7 @@ namespace cimg_library_suffixed { 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); @@ -41429,23 +43971,27 @@ namespace cimg_library_suffixed { const bool release_camera=true, const unsigned int capture_width=0, const unsigned int capture_height=0) { #ifdef cimg_use_opencv - if (camera_index>255) + if (camera_index>99) throw CImgArgumentException(_cimg_instance "load_camera(): Invalid request for camera #%u " - "(no more than 256 cameras can be managed).", + "(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) { cimg::mutex(9); if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index])); capture[camera_index] = 0; + capture_w[camera_index] = capture_h[camera_index] = 0; cimg::mutex(9,0); return *this; } if (!capture[camera_index]) { cimg::mutex(9); capture[camera_index] = cvCreateCameraCapture(camera_index); + capture_w[camera_index] = 0; + capture_h[camera_index] = 0; cimg::mutex(9,0); if (!capture[camera_index]) { throw CImgIOException(_cimg_instance @@ -41455,8 +44001,14 @@ namespace cimg_library_suffixed { } } cimg::mutex(9); - if (capture_width) cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width); - if (capture_height) cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height); + if (capture_width!=capture_w[camera_index]) { + cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width); + capture_w[camera_index] = capture_width; + } + if (capture_height!=capture_h[camera_index]) { + cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height); + capture_h[camera_index] = capture_height; + } const IplImage *img = 0; for (unsigned int i = 0; i",pixel_type()); + 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,cimg::t_normal, + 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, mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), @@ -41580,10 +44132,10 @@ namespace cimg_library_suffixed { if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); } } if (!is_empty() && display_stats) - std::fprintf(cimg::output(), + 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[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]), @@ -41608,8 +44160,9 @@ namespace cimg_library_suffixed { \param disp Display window. \param display_info Tells if image information are displayed on the standard output. **/ - const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0) const { - return _display(disp,0,display_info,XYZ,false); + 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 image into an interactive window. @@ -41617,16 +44170,18 @@ namespace cimg_library_suffixed { \param title Window title \param display_info Tells if image information are displayed on the standard output. **/ - const CImg& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0) const { + 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,XYZ,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, unsigned int *const XYZ, + 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; + 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); @@ -41684,14 +44239,21 @@ namespace cimg_library_suffixed { _XYZ[1] = (unsigned int)(y1 - y0)/2; _XYZ[2] = (unsigned int)(z1 - z0)/2; } - const CImg selection = visu._get_select(disp,0,2,_XYZ,x0,y0,z0,is_first_select,_depth>1); + + disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; + const CImg selection = visu._get_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_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; - } 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_down = !(go_up = disp.wheel()>0); } + } disp.set_wheel(); } @@ -41849,6 +44411,10 @@ namespace cimg_library_suffixed { else { z0+=(depth() - 1 - z1); z1 = depth() - 1; } } disp.wait(100); + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } } disp.set_key(key); if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; } @@ -41886,11 +44452,12 @@ namespace cimg_library_suffixed { 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 { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static, - render_motion,is_double_sided,focale, + render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -41905,12 +44472,13 @@ namespace cimg_library_suffixed { 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 { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static, - render_motion,is_double_sided,focale, + render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -41924,17 +44492,18 @@ namespace cimg_library_suffixed { 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 { + 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,is_double_sided,focale, + render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(const char *const title, - const CImg& vertices, + const CImg& vertices, const CImgList& primitives, const CImgList& colors, const bool centering=true, @@ -41942,11 +44511,12 @@ namespace cimg_library_suffixed { 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 { + 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,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -41959,29 +44529,31 @@ namespace cimg_library_suffixed { 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 { + 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,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(const char *const title, - const CImg& vertices, + const CImg& vertices, const CImgList& primitives, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -41993,57 +44565,60 @@ namespace cimg_library_suffixed { 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 { + 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,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. template const CImg& display_object3d(const char *const title, - const CImg& vertices, + const CImg& vertices, const bool centering=true, const int render_static=4, const int render_motion=1, const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + 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,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } template const CImg& _display_object3d(CImgDisplay& disp, const char *const title, - const CImg& vertices, - const CImgList& primitives, - const CImgList& colors, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, const to& opacities, - const bool centering, - const int render_static, const int render_motion, - const bool is_double_sided, const float focale, + const bool centering, + const int render_static, const int render_motion, + const bool is_double_sided, const float focale, const float light_x, const float light_y, const float light_z, - const float specular_lightness, const float specular_shininess, - const bool display_axes, float *const pose_matrix) const { + 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, + 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,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); - else return CImg(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2, + 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,is_double_sided,focale, + render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } else { if (disp) disp.resize(*this,false); } CImg error_message(1024); if (!vertices.is_object3d(primitives,colors,opacities,true,error_message)) @@ -42054,12 +44629,12 @@ namespace cimg_library_suffixed { CImgList nprimitives(vertices._width,1,1,1,1); cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l; return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, - render_static,render_motion,is_double_sided,focale, + render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } if (!disp) { - disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)", pixel_type(),vertices._width,primitives._width); } else if (title) disp.set_title("%s",title); @@ -42257,6 +44832,7 @@ namespace cimg_library_suffixed { } } 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 : @@ -42335,43 +44911,40 @@ namespace cimg_library_suffixed { } 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++); + 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,0.7f,13).display(disp); visu.save(filename); (+visu).draw_text(0,0," Snapshot '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + 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++); + 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,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).display(disp); + 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); } while (file); @@ -42380,16 +44953,15 @@ namespace cimg_library_suffixed { 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).display(disp); + 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++); + 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... ", @@ -42405,15 +44977,14 @@ namespace cimg_library_suffixed { sprite_scale); board.saveEPS(filename); (+visu).draw_text(0,0," Object '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + 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++); + 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... ", @@ -42429,7 +45000,7 @@ namespace cimg_library_suffixed { sprite_scale); board.saveSVG(filename); (+visu).draw_text(0,0," Object '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp); disp.set_key(key,false); key = 0; } break; #endif @@ -42439,6 +45010,10 @@ namespace cimg_library_suffixed { 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)); @@ -42463,23 +45038,26 @@ namespace cimg_library_suffixed { 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 { - return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); + 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 { + 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); + 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 { + 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.", @@ -42502,11 +45080,11 @@ namespace cimg_library_suffixed { if (y0==y1) { --y0; ++y1; } const CImg selection = zoom.get_select_graph(disp,plot_type,vertex_type, - labelx, + labelx, nxmin + x0*(nxmax - nxmin)/siz1, nxmin + x1*(nxmax - nxmin)/siz1, - labely,y0,y1); - const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + 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 { @@ -42534,9 +45112,9 @@ namespace cimg_library_suffixed { 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; } @@ -42589,6 +45167,11 @@ namespace cimg_library_suffixed { 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; @@ -42762,7 +45345,7 @@ namespace cimg_library_suffixed { cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); CImg varname(1024); *varname = 0; - if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data); + 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" @@ -42850,7 +45433,8 @@ namespace cimg_library_suffixed { 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(), @@ -42880,7 +45464,7 @@ namespace cimg_library_suffixed { 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), @@ -42960,7 +45544,7 @@ namespace cimg_library_suffixed { 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; @@ -42992,13 +45576,13 @@ namespace cimg_library_suffixed { switch (_spectrum) { case 1 : { // Greyscale images const T *ptr_g = data(0, cinfo.next_scanline); - for(unsigned int b = 0; b < cinfo.image_width; b++) + for (unsigned int b = 0; b hname(1024), iname(1024); + CImg header(348,1,1,1,0), 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,hname._width,"%s.hdr",filename); cimg_snprintf(iname,iname._width,"%s.img",filename); @@ -43941,27 +46523,27 @@ namespace cimg_library_suffixed { if (!cimg::strncasecmp(ext,"hdr",3)) { std::strcpy(hname,filename); std::strncpy(iname,filename,iname._width - 1); - std::sprintf(iname._data + std::strlen(iname) - 3,"img"); + cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); } if (!cimg::strncasecmp(ext,"img",3)) { std::strcpy(hname,filename); std::strncpy(iname,filename,iname._width - 1); - std::sprintf(hname._data + std::strlen(iname) - 3,"hdr"); + cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); } if (!cimg::strncasecmp(ext,"nii",3)) { std::strncpy(hname,filename,hname._width - 1); *iname = 0; } - int *const iheader = (int*)header; + 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] = (short)_width; - ((short*)(header + 40))[2] = (short)_height; - ((short*)(header + 40))[3] = (short)_depth; - ((short*)(header + 40))[4] = (short)_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; @@ -43979,17 +46561,17 @@ namespace cimg_library_suffixed { 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; + ((short*)&(header[70]))[0] = datatype; + ((short*)&(header[72]))[0] = sizeof(T); + ((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; + ((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,348,file); if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } cimg::fwrite(_data,size(),file); cimg::fclose(file); @@ -44031,9 +46613,9 @@ namespace cimg_library_suffixed { //! 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, - const unsigned int z0, const unsigned int c0) const { + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { CImgList(*this,true).save_cimg(file,n0,x0,y0,z0,c0); return *this; } @@ -44120,14 +46702,14 @@ namespace cimg_library_suffixed { 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", + 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+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxel_size[0],voxel_size[1],voxel_size[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); + 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; @@ -44226,28 +46808,28 @@ namespace cimg_library_suffixed { 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; } return nbdims; } @@ -44266,22 +46848,22 @@ namespace cimg_library_suffixed { #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \ if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \ (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \ - unsigned int *iheader = (unsigned int*)(header + 12); \ - nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ - cimg::fwrite(header,36,nfile); \ + unsigned int *iheader = (unsigned int*)(header + 12); \ + nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ + cimg::fwrite(header,36,nfile); \ if (sizeof(unsigned long)==4) { CImg 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); \ + 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); \ + 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'.",\ cimg_instance, \ filename?filename:"(FILE*)"); \ - if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ + if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ __cimg_save_pandore_case(unsigned char); \ - } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ + } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \ else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \ else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \ @@ -44289,7 +46871,7 @@ namespace cimg_library_suffixed { "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) { \ + } 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 \ @@ -44297,7 +46879,7 @@ namespace cimg_library_suffixed { cimg_instance, \ filename?filename:"(FILE*)"); \ } \ - saved = true; \ + saved = true; \ } if (!file && !filename) @@ -44307,8 +46889,9 @@ namespace cimg_library_suffixed { 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 }; + static const unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, + 0,0,0,0,'C','I','m','g',0,0,0,0,0, + 'N','o',' ','d','a','t','e',0,0,0,0 }; unsigned int nbdims, dims[5] = { 0 }; bool saved = false; _cimg_save_pandore_case(1,1,1,"unsigned char",2); @@ -45084,16 +47667,16 @@ namespace cimg_library_suffixed { 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 long siz = (unsigned long)width*height*depth*spectrum, nsiz = siz*n; \ - T *ptrd = _data->_data; \ - va_list ap; \ - va_start(ap,val1); \ - for (unsigned long l = 0, s = 0, i = 0; i_data; \ + va_list ap; \ + va_start(ap,val1); \ + for (unsigned long l = 0, s = 0, i = 0; i& const_empty() { + static const CImgList _empty; + return _empty; + } + //@} //------------------------------------------ // @@ -47154,8 +49743,9 @@ namespace cimg_library_suffixed { \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 _get_select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false); } //! Display a simple interactive interface to select images or sublists. @@ -47167,13 +49757,14 @@ namespace cimg_library_suffixed { \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 _get_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 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()) @@ -47212,7 +49803,7 @@ namespace cimg_library_suffixed { 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; @@ -47221,6 +49812,7 @@ namespace cimg_library_suffixed { 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. @@ -47324,6 +49916,7 @@ namespace cimg_library_suffixed { 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 : @@ -47353,10 +49946,9 @@ namespace cimg_library_suffixed { } 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++); + 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) { @@ -47364,20 +49956,19 @@ namespace cimg_library_suffixed { 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).display(disp); + 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); } while (file); @@ -47385,13 +49976,17 @@ namespace cimg_library_suffixed { foreground_color,background_color,0.7f,13).display(disp); save(filename); (+visu0).draw_text(0,0," Instance '%s' saved. ", - foreground_color,background_color,0.7f,13,filename).display(disp); + 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; }} + 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) { @@ -47422,6 +50017,7 @@ namespace cimg_library_suffixed { 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); @@ -47489,23 +50085,26 @@ namespace cimg_library_suffixed { pixel_type()); } catch (CImgIOException&) { std::FILE *file = 0; - try { - file = cimg::fopen(filename,"rb"); - } catch (CImgIOException&) { - cimg::exception_mode(omode); - throw CImgIOException(_cimglist_instance - "load(): Failed to open file '%s'.", - cimglist_instance, - filename); - } + if (!is_stdin) try { + file = cimg::fopen(filename,"rb"); + } catch (CImgIOException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimglist_instance + "load(): Failed to open file '%s'.", + cimglist_instance, + filename); + } try { - const char *const f_type = cimg::file_type(file,filename); - std::fclose(file); - if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); - else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); - else throw CImgIOException("CImgList<%s>::load()", - pixel_type()); + if (!is_stdin) { + const char *const f_type = cimg::ftype(file,filename); + std::fclose(file); + if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else throw CImgIOException("CImgList<%s>::load()", + pixel_type()); + } else throw CImgIOException("CImgList<%s>::load()", + pixel_type()); } catch (CImgIOException&) { assign(1); try { @@ -47579,7 +50178,7 @@ namespace cimg_library_suffixed { for (unsigned int l = 0; l=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 #%lu",&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'.", \ cimglist_instance, \ @@ -47621,8 +50220,8 @@ namespace cimg_library_suffixed { do { j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; } while (*tmp=='#' && i>=0); - err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]", - &N,str_pixeltype._data,str_endian._data); + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]", + &N,str_pixeltype._data,str_endian._data); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimglist_instance @@ -47714,17 +50313,17 @@ namespace cimg_library_suffixed { } 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 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 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'", \ cimglist_instance, \ @@ -47798,7 +50397,7 @@ namespace cimg_library_suffixed { 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 = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]", + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]", &N,str_pixeltype._data,str_endian._data); if (err<2) { if (!file) cimg::fclose(nfile); @@ -48002,9 +50601,9 @@ namespace cimg_library_suffixed { } CImgList& _load_yuv(std::FILE *const file, const char *const filename, - 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) { + 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).", @@ -48021,9 +50620,9 @@ namespace cimg_library_suffixed { size_x,size_y,filename?filename:"(FILE*)"); const unsigned int - nfirst_frame = first_frame 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"); @@ -48072,7 +50671,7 @@ namespace cimg_library_suffixed { } if (yuv2rgb) tmp.YCbCrtoRGB(); insert(tmp); - if (nstep_frame>1) std::fseek(nfile,(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + if (nstep_frame>1) std::fseek(nfile,(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); } } } @@ -48092,7 +50691,7 @@ namespace cimg_library_suffixed { \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 open or released without any frames read. + \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, @@ -48135,7 +50734,7 @@ namespace cimg_library_suffixed { } else if (filename) cimg::warn(_cimglist_instance - "load_video() : File '%s', opened video stream associated with filename not found.", + "load_video() : File '%s', no opened video stream associated with filename found.", cimglist_instance,filename); else cimg::warn(_cimglist_instance @@ -48442,7 +51041,7 @@ namespace cimg_library_suffixed { **/ template CImgList& load_off(const char *const filename, - CImgList& primitives, CImgList& colors) { + CImgList& primitives, CImgList& colors) { return get_load_off(filename,primitives,colors).move_to(*this); } @@ -48461,13 +51060,13 @@ namespace cimg_library_suffixed { \param step_frame Step applied between each frame. **/ 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 first_frame=0, const unsigned int last_frame=~0U, + 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(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 first_frame=0, const unsigned int last_frame=~0U, + 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); @@ -48534,10 +51133,10 @@ namespace cimg_library_suffixed { cimglist_for(*this,l) msiz+=_data[l].size(); msiz*=sizeof(T); const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U; - char _title[64] = { 0 }; - if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type()); + 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,cimg::t_normal, + 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)), @@ -48585,9 +51184,9 @@ namespace cimg_library_suffixed { **/ const CImgList& display(CImgDisplay &disp, const bool display_info, const char axis='x', const float align=0, - unsigned int *const XYZ=0) const { + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { bool is_exit = false; - return _display(disp,0,display_info,axis,align,XYZ,0,true,is_exit); + return _display(disp,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); } //! Display the current CImgList instance in a new display window. @@ -48599,15 +51198,16 @@ namespace cimg_library_suffixed { **/ const CImgList& display(const char *const title=0, const bool display_info=true, const char axis='x', const float align=0, - unsigned int *const XYZ=0) const { + 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,XYZ,0,true,is_exit); + return _display(disp,title,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, unsigned int *const XYZ, - const unsigned int orig, const bool is_first_call, bool &is_exit) const { + 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.", @@ -48648,13 +51248,13 @@ namespace cimg_library_suffixed { 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,XYZ,!is_first_call); + _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call); if (disp.key()) is_exit = true; disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data()); } else { bool disp_resize = !is_first_call; while (!disp.is_closed() && !is_exit) { - const CImg s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true); + const CImg s = _get_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 (disp.button()&2) { disp.flush(); break; } @@ -48669,10 +51269,12 @@ namespace cimg_library_suffixed { 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,XYZ,orig + ind0,false,is_exit); + get_shared_images(ind0,ind1)._display(disp,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,XYZ,orig + s[0],false,is_exit); + get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,XYZ,exit_on_anykey, + orig + s[0],false,is_exit); } } return *this; @@ -48806,7 +51408,7 @@ namespace cimg_library_suffixed { \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 unsigned int fps=25, + 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; @@ -48832,15 +51434,15 @@ namespace cimg_library_suffixed { } #if cimg_OS!=2 - cimg_snprintf(command,command._width,"%s -delay 1x%u -loop %u", - cimg::imagemagick_path(),fps,nb_loops); + cimg_snprintf(command,command._width,"%s -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)cimg::max(0.0f,cimg::round(100/fps)),nb_loops); CImg::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 - cimg_snprintf(command,command._width,"\"%s -delay 1x%u -loop %u", - cimg::imagemagick_path(),fps,nb_loops); + cimg_snprintf(command,command._width,"\"%s -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)cimg::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()); @@ -48985,7 +51587,7 @@ namespace cimg_library_suffixed { for (unsigned int l = 0; l nfilename(1024); cimglist_for(*this,l) { cimg::number_filename(body,l,6,nfilename); - if (*ext) std::sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); + if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); _data[l].save_gzip_external(nfilename); } } @@ -49363,6 +51965,7 @@ namespace cimg_library_suffixed { } else { CImg _src(src,false); _src.channels(0,cimg::min(_src._spectrum - 1,2U)).resize(W,H); + _src.resize(W,H,1,3,_src._spectrum==1?1:0); 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) { @@ -49486,7 +52089,7 @@ namespace cimg_library_suffixed { if (is_compressed) { #ifdef cimg_use_zlib const unsigned long siz = sizeof(T)*ref.size(); - unsigned long csiz = siz + siz/100 + 16; + unsigned long csiz = compressBound(siz); Bytef *const cbuf = new Bytef[csiz]; if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) cimg::warn(_cimglist_instance @@ -49543,7 +52146,7 @@ namespace cimg_library_suffixed { j = 0; while ((i=(int)*stream)!='\n' && stream::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \ "image #%u in serialized buffer.", \ pixel_type(),W,H,D,C,l); \ @@ -49579,7 +52182,7 @@ namespace cimg_library_suffixed { j = 0; while ((i=(int)*stream)!='\n' && stream::get_unserialize(): CImg header not found in serialized buffer.", @@ -49646,13 +52249,13 @@ namespace cimg_library_suffixed { \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width. **/ static const CImgList& font(const unsigned int font_height, const bool is_variable_width=true) { - if (!font_height) return CImgList::empty(); + if (!font_height) return CImgList::const_empty(); cimg::mutex(11); // Decompress nearest base font data if needed. - const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 }; - const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 }, - data_Ms[] = { 86,79,57,47 }; + 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]; @@ -49793,736 +52396,935 @@ namespace cimg_library_suffixed { namespace cimg { - //! Get/set path to store temporary files. - /** - \param user_path Specified path, or \c 0 to get the path currently used. - \param reinit_path Force path to be recalculated (may take some time). - \return Path where temporary files can be saved. - **/ - inline const char* temporary_path(const char *const user_path, const bool reinit_path) { -#define _cimg_test_temporary_path(p) \ - if (!path_found) { \ - cimg_snprintf(s_path,s_path.width(),"%s",p); \ - cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ - if ((file=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); + //! Get/set path to store temporary files. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path where temporary files can be saved. + **/ + inline const char* temporary_path(const char *const user_path, const bool reinit_path) { +#define _cimg_test_temporary_path(p) \ + if (!path_found) { \ + cimg_snprintf(s_path,s_path.width(),"%s",p); \ + cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ + if ((file=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:"); + _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"); + _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"); - } + 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; } + 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. - **/ + //! 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). + 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"); - } + 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"); + std::strcpy(s_path,"C:\\PROGRA~1"); #endif - } - cimg::mutex(7,0); - return s_path; } + 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; + //! 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(); - if (!path_found) { - std::strcpy(s_path,".\\convert.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-\\convert.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\\IMAGEM~1.%d-Q\\convert.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\\IMAGEM~1.%d\\convert.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\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.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\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.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\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%.2d-\\convert.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:\\IMAGEM~1.%d-Q\\convert.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:\\IMAGEM~1.%d\\convert.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:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%.2d-\\convert.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:\\IMAGEM~1.%d-Q\\convert.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:\\IMAGEM~1.%d\\convert.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:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"convert.exe"); + const char *const pf_path = programfiles_path(); + if (!path_found) { + std::strcpy(s_path,".\\convert.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-\\convert.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\\IMAGEM~1.%d-Q\\convert.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\\IMAGEM~1.%d\\convert.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\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.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\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.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\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%.2d-\\convert.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:\\IMAGEM~1.%d-Q\\convert.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:\\IMAGEM~1.%d\\convert.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:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%.2d-\\convert.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:\\IMAGEM~1.%d-Q\\convert.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:\\IMAGEM~1.%d\\convert.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:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.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:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"convert.exe"); #else - 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); + if (!path_found) { + std::strcpy(s_path,"./convert"); + if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } } - cimg::mutex(7,0); - return s_path; + 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; + //! 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"); + 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); + if (!path_found) { + std::strcpy(s_path,"./gm"); + if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } } - cimg::mutex(7,0); - return s_path; + 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; + //! 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"); + 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); + if (!path_found) { + std::strcpy(s_path,"./medcon"); + if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } } - cimg::mutex(7,0); - return s_path; + 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; + //! 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"); + 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); + if (!path_found) { + std::strcpy(s_path,"./ffmpeg"); + if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } } - cimg::mutex(7,0); - return s_path; + 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; + //! 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"); + 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); + if (!path_found) { + std::strcpy(s_path,"./gzip"); + if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } } - cimg::mutex(7,0); - return s_path; + 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; + //! 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"); + 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); + if (!path_found) { + std::strcpy(s_path,"./gunzip"); + if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } } - cimg::mutex(7,0); - return s_path; + 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; + //! 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"); + 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"); + 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); + 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; } } - cimg::mutex(7,0); - return s_path; + 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 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; + //! 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,".\\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"); + 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,"./wget"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } - } - if (!path_found) std::strcpy(s_path,"wget"); + 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 - winformat_string(s_path); + } + + // 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; } - cimg::mutex(7,0); - return s_path; + lp = (unsigned int)std::strlen(_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; + // Windows version. #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"); + 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). #else - if (!path_found) { - std::strcpy(s_path,"./curl"); - if ((file=std::fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + 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); + } } - if (!path_found) std::strcpy(s_path,"curl"); -#endif - winformat_string(s_path); } - cimg::mutex(7,0); - return s_path; } + closedir(dir); +#endif - //! Load file from network as a local temporary file. - /** - \param filename 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. - \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) { - 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); + // Sort resulting list by lexicographic order. + if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg),_sort_files); -#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); - res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - std::fseek(file,0,SEEK_END); // Check if file size is 0. - const long siz = std::ftell(file); - cimg::fclose(file); - if (siz>0 && res==CURLE_OK) { - cimg::exception_mode(omode); - return filename_local; - } else std::remove(filename_local); + 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 (...) { } - cimg::exception_mode(omode); - if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url); + } + } catch (CImgIOException&) { } + cimg::exception_mode(omode); + return f_type; + } + + //! Load file from network as a local temporary file. + /** + \param filename 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. + \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); + std::fseek(file,0,SEEK_END); // Check if file size is 0. + const long siz = std::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); + CImg command((unsigned int)std::strlen(url) + 64); + cimg::unused(try_fallback); - // Try with 'curl' first. - if (timeout) + // 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); + } + cimg::system(command); - if (!(file = std::fopen(filename_local,"rb"))) { + if (!(file = std::fopen(filename_local,"rb"))) { - // Try with 'wget' otherwise. - if (timeout) + // 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); + } + 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); + 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. + // 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(filename_local,command); - cimg_snprintf(command,command._width,"%s --quiet \"%s.gz\"", - gunzip_path(),filename_local); - cimg::system(command); + std::rename(command,filename_local); 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"); - } } - std::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; } + std::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 unsigned long tictoc(const bool is_tic) { - cimg::mutex(2); - static CImg times(64); - static unsigned int pos = 0; - const unsigned long 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 unsigned long - 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); + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline unsigned long tictoc(const bool is_tic) { + cimg::mutex(2); + static CImg times(64); + static unsigned int pos = 0; + const unsigned long 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 unsigned long + 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 && !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); + 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{ - 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); - } + 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; } + cimg::mutex(2,0); + return dt; + } // Return a temporary string describing the size of a memory buffer. inline const char *strbuffersize(const unsigned long size) { @@ -50554,7 +53356,7 @@ namespace cimg { - 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. + At least one button must be specified. **/ template inline int dialog(const char *const title, const char *const msg, @@ -50567,7 +53369,7 @@ namespace cimg { 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 @@ -50657,7 +53459,7 @@ namespace cimg { // Open window and enter events loop CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false); if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2, - (CImgDisplay::screen_height() - disp.height())/2); + (CImgDisplay::screen_height() - disp.height())/2); bool stop_flag = false, refresh = false; int oselected = -1, oclicked = -1, selected = -1, clicked = -1; while (!disp.is_closed() && !stop_flag) { @@ -50727,8 +53529,8 @@ namespace cimg { \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. + 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) { -- 2.26.2