diff --git a/external/CImg/.github/workflows/Test_Build.yml b/external/CImg/.github/workflows/Test_Build.yml new file mode 100644 index 000000000..77bc8a2c4 --- /dev/null +++ b/external/CImg/.github/workflows/Test_Build.yml @@ -0,0 +1,37 @@ +name: Build then test + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build-on-ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install dependencies + run: | + sudo apt-get install -y gcc g++ clang libpng-dev libjpeg-dev libmagick++-dev + sudo apt-get install -y libgraphicsmagick++-dev libfftw3-dev zlib1g-dev libheif-dev + - name: build + run: | + pushd examples + make clean + make mlinux + popd + + build-on-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - name: Install dependencies + run: | + brew install libheif libpng fftw libjpeg gcc zlib imagemagick automake openexr llvm + - name: build + run: | + pushd examples + make clean + make mmacos + popd diff --git a/external/CImg/CImg.h b/external/CImg/CImg.h index a8b893a37..9ac9175b0 100644 --- a/external/CImg/CImg.h +++ b/external/CImg/CImg.h @@ -54,7 +54,7 @@ // Set version number of the library. #ifndef cimg_version -#define cimg_version 297 +#define cimg_version 320 /*----------------------------------------------------------- # @@ -148,19 +148,16 @@ // 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 @@ -211,8 +208,21 @@ enum {FALSE_WIN = 0}; #define cimg_pragma(x) _Pragma(#x) #endif -// Define own types 'cimg_long/ulong' and 'cimg_int64/uint64' to ensure portability. -// ( constrained to 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' and 'sizeof(cimg_int64/cimg_uint64)=8' ). +// Define own datatypes to ensure portability. +// ( 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' ). +#define cimg_uint8 unsigned char +#if defined(CHAR_MAX) && CHAR_MAX==255 +#define cimg_int8 signed char +#else +#define cimg_int8 char +#endif +#define cimg_uint16 unsigned short +#define cimg_int16 short +#define cimg_uint32 unsigned int +#define cimg_int32 int +#define cimg_float32 float +#define cimg_float64 double + #if cimg_OS==2 #define cimg_uint64 unsigned __int64 @@ -251,6 +261,14 @@ enum {FALSE_WIN = 0}; #endif +#ifndef cimg_max_buf_size +#if UINTPTR_MAX==0xffffffff +#define cimg_max_buf_size ((cimg_ulong)3*1024*1024*1024) +#else +#define cimg_max_buf_size ((cimg_ulong)16*1024*1024*1024) +#endif +#endif + // Configure filename separator. // // Filename separator is set by default to '/', except for Windows where it is '\'. @@ -297,6 +315,9 @@ enum {FALSE_WIN = 0}; #else #define cimg_use_openmp 0 #endif +#else +#undef cimg_use_openmp +#define cimg_use_openmp 1 #endif #if cimg_use_openmp!=0 #include @@ -315,18 +336,20 @@ enum {FALSE_WIN = 0}; // Define abort macros to be used with OpenMP. #ifndef _cimg_abort_init_openmp -#define _cimg_abort_init_openmp bool _cimg_abort_go_openmp = true; cimg::unused(_cimg_abort_go_openmp) +#define _cimg_abort_init_openmp unsigned int _cimg_abort_go_openmp = 1; cimg::unused(_cimg_abort_go_openmp) #endif #ifndef _cimg_abort_try_openmp #define _cimg_abort_try_openmp if (_cimg_abort_go_openmp) try #endif #ifndef _cimg_abort_catch_openmp -#define _cimg_abort_catch_openmp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } +#define _cimg_abort_catch_openmp catch (CImgAbortException&) { \ + cimg_pragma_openmp(atomic) _cimg_abort_go_openmp&=0; \ +} #endif #ifndef _cimg_abort_catch_fill_openmp #define _cimg_abort_catch_fill_openmp \ - catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg::string(e._message).move_to(is_error); \ - cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } + catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg::string(e._message).move_to(is_error_expr); \ + cimg_pragma_openmp(atomic) _cimg_abort_go_openmp&=0; } #endif #ifdef cimg_abort_test2 #ifndef _cimg_abort_try_openmp2 @@ -648,18 +671,6 @@ extern "C" { #define _cimg_redefine_PI #endif -// Define 'cimg_library' namespace suffix. -// -// You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work -// with several versions of the library at the same time. -#ifdef cimg_namespace_suffix -#define __cimg_library_suffixed(s) cimg_library_##s -#define _cimg_library_suffixed(s) __cimg_library_suffixed(s) -#define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix) -#else -#define cimg_library_suffixed cimg_library -#endif - /*------------------------------------------------------------------------------ # # Define user-friendly macros. @@ -670,9 +681,9 @@ extern "C" { ------------------------------------------------------------------------------*/ // Macros to define program usage, and retrieve command line arguments. -#define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false) -#define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0) -#define cimg_option(name,_default,usage) cimg_library_suffixed::cimg::option(name,argc,argv,_default,usage) +#define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage,false) +#define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0) +#define cimg_option(name,_default,usage) cimg_library::cimg::option(name,argc,argv,_default,usage) // Macros to define and manipulate local neighborhoods. #define CImg_2x2(I,T) T I[4]; \ @@ -2292,7 +2303,7 @@ extern "C" { \endcode to simplify the declaration of \CImg Library objects afterwards. **/ -namespace cimg_library_suffixed { +namespace cimg_library { // Declare the four classes of the CImg Library. template struct CImg; @@ -2376,7 +2387,7 @@ namespace cimg_library_suffixed { return result; } - // Mutex-protected version of sscanf, sprintf and snprintf. + // Mutex-protected version of sscanf, snprintf and vnsprintf. // 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, ...) { @@ -2389,16 +2400,6 @@ namespace cimg_library_suffixed { 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; @@ -2551,7 +2552,7 @@ namespace cimg_library_suffixed { In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown. This may lead the program to break (this is the default behavior), but you can bypass this behavior by handling the exceptions by yourself, - using a usual try { ... } catch () { ... } bloc, as in the following example: + using a usual try { ... } catch () { ... } block, as in the following example: \code #define "CImg.h" using namespace cimg_library; @@ -2573,16 +2574,16 @@ namespace cimg_library_suffixed { int size = cimg_vsnprintf(0,0,format,ap2); \ if (size++>=0) { \ delete[] _message; \ - _message = new char[size]; \ - cimg_vsnprintf(_message,size,format,ap); \ + _message = new char[(size_t)size]; \ + cimg_vsnprintf(_message,(size_t)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(); \ + if (cimg::exception_mode()>=3) cimg_library::cimg::info(); \ } \ } \ - va_end(ap); va_end(ap2); \ + va_end(ap); va_end(ap2); char *_message; CImgException() { _message = new char[1]; *_message = 0; } @@ -2699,7 +2700,10 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "bool"; return s; } + static const char* string() { + static const char *const s = "bool"; + return s; + } static bool is_float() { return false; } static bool is_inf(const bool) { return false; } static bool is_nan(const bool) { return false; } @@ -2715,7 +2719,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "unsigned char"; return s; } + static const char* string() { static const char *const s = "uint8"; return s; } static bool is_float() { return false; } static bool is_inf(const unsigned char) { return false; } static bool is_nan(const unsigned char) { return false; } @@ -2732,7 +2736,7 @@ namespace cimg_library_suffixed { #if defined(CHAR_MAX) && CHAR_MAX==255 template<> struct type { - static const char* string() { static const char *const s = "char"; return s; } + static const char* string() { static const char *const s = "uint8"; return s; } static bool is_float() { return false; } static bool is_inf(const char) { return false; } static bool is_nan(const char) { return false; } @@ -2748,7 +2752,7 @@ namespace cimg_library_suffixed { }; #else template<> struct type { - static const char* string() { static const char *const s = "char"; return s; } + static const char* string() { static const char *const s = "int8"; return s; } static bool is_float() { return false; } static bool is_inf(const char) { return false; } static bool is_nan(const char) { return false; } @@ -2764,7 +2768,7 @@ namespace cimg_library_suffixed { #endif template<> struct type { - static const char* string() { static const char *const s = "signed char"; return s; } + static const char* string() { static const char *const s = "int8"; return s; } static bool is_float() { return false; } static bool is_inf(const signed char) { return false; } static bool is_nan(const signed char) { return false; } @@ -2780,7 +2784,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "unsigned short"; return s; } + static const char* string() { static const char *const s = "uint16"; return s; } static bool is_float() { return false; } static bool is_inf(const unsigned short) { return false; } static bool is_nan(const unsigned short) { return false; } @@ -2796,7 +2800,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "short"; return s; } + static const char* string() { static const char *const s = "int16"; return s; } static bool is_float() { return false; } static bool is_inf(const short) { return false; } static bool is_nan(const short) { return false; } @@ -2811,7 +2815,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "unsigned int"; return s; } + static const char* string() { static const char *const s = "uint32"; return s; } static bool is_float() { return false; } static bool is_inf(const unsigned int) { return false; } static bool is_nan(const unsigned int) { return false; } @@ -2827,13 +2831,13 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "int"; return s; } + static const char* string() { static const char *const s = "int32"; return s; } static bool is_float() { return false; } static bool is_inf(const int) { return false; } static bool is_nan(const int) { return false; } static bool is_finite(const int) { return true; } static int min() { return ~max(); } - static int max() { return (int)((unsigned int)-1>>1); } + static int max() { return (int)(~0U>>1); } static int inf() { return max(); } static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } static const char* format() { return "%d"; } @@ -2842,7 +2846,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "unsigned int64"; return s; } + static const char* string() { static const char *const s = "uint64"; return s; } static bool is_float() { return false; } static bool is_inf(const cimg_uint64) { return false; } static bool is_nan(const cimg_uint64) { return false; } @@ -2854,7 +2858,7 @@ namespace cimg_library_suffixed { return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } static const char* format() { return cimg_fuint64; } static const char* format_s() { return cimg_fuint64; } - static unsigned long format(const cimg_uint64 val) { return (unsigned long)val; } + static cimg_uint64 format(const cimg_uint64 val) { return val; } }; template<> struct type { @@ -2875,7 +2879,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "double"; return s; } + static const char* string() { static const char *const s = "float64"; return s; } static bool is_float() { return true; } static bool is_inf(const double val) { #ifdef isinf @@ -2926,7 +2930,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "float"; return s; } + static const char* string() { static const char *const s = "float32"; return s; } static bool is_float() { return true; } static bool is_inf(const float val) { #ifdef isinf @@ -2966,7 +2970,7 @@ namespace cimg_library_suffixed { }; template<> struct type { - static const char* string() { static const char *const s = "long double"; return s; } + static const char* string() { static const char *const s = "float128"; return s; } static bool is_float() { return true; } static bool is_inf(const long double val) { #ifdef isinf @@ -3001,7 +3005,7 @@ namespace cimg_library_suffixed { #ifdef cimg_use_half template<> struct type { - static const char* string() { static const char *const s = "half"; return s; } + static const char* string() { static const char *const s = "float16"; return s; } static bool is_float() { return true; } static bool is_inf(const long double val) { #ifdef isinf @@ -5715,6 +5719,7 @@ namespace cimg_library_suffixed { #ifdef cimg_no_system_calls return -1; #else + if (is_verbose) return std::system(command); #if cimg_OS==1 const unsigned int l = (unsigned int)std::strlen(command); @@ -6208,6 +6213,18 @@ namespace cimg_library_suffixed { return val*val; } + // Return inverse of error function. + template + inline T erfinv(const T& val) { + const T + sgn = val<0?-1:1, + x = (1 - val)*(1 + val), + lnx = std::log(x), + tt1 = (T)(2/(cimg::PI*0.147) + 0.5*lnx), + tt2 = lnx/(T)0.147; + return sgn*std::sqrt(-tt1 + std::sqrt(tt1*tt1 - tt2)); + } + //! Return cubic root of a value. template inline double cbrt(const T& x) { @@ -6295,18 +6312,22 @@ namespace cimg_library_suffixed { **/ template inline T mod(const T& x, const T& m) { + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); const double dx = (double)x, dm = (double)m; if (!cimg::type::is_finite(dm)) return x; if (cimg::type::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm)); return (T)0; } inline int mod(const bool x, const bool m) { - return m?(x?1:0):0; + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + return x?1:0; } inline int mod(const unsigned char x, const unsigned char m) { + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); return x%m; } inline int mod(const char x, const char m) { + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); #if defined(CHAR_MAX) && CHAR_MAX==255 return x%m; #else @@ -6314,22 +6335,28 @@ namespace cimg_library_suffixed { #endif } inline int mod(const unsigned short x, const unsigned short m) { - return x%m; + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + return (int)(x%m); } inline int mod(const short x, const short m) { - return x>=0?x%m:(x%m?m + x%m:0); + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + return (int)(x>=0?x%m:(x%m?m + x%m:0)); } inline int mod(const unsigned int x, const unsigned int m) { + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); return (int)(x%m); } inline int mod(const int x, const int m) { - return x>=0?x%m:(x%m?m + x%m:0); + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + return (int)(x>=0?x%m:(x%m?m + x%m:0)); } inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) { - return x%m; + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + return (cimg_int64)(x%m); } inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) { - return x>=0?x%m:(x%m?m + x%m:0); + if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + return (cimg_int64)(x>=0?x%m:(x%m?m + x%m:0)); } //! Return the min-mod of two values. @@ -6847,7 +6874,20 @@ namespace cimg_library_suffixed { //! Return \c true if input character is blank (space, tab, or non-printable character). inline bool is_blank(const char c) { - return c>=0 && (unsigned char)c<=' '; + return (unsigned char)c<=' '; + } + + // Return \c true if specified argument is in set \c [a-zA-Z0-9_]. + inline bool is_varchar(const char c) { + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; + } + + //! Return \c true if argument \p str can be considered as a regular variable name. + inline bool is_varname(const char *const str, const unsigned int length=~0U) { + if (*str>='0' && *str<='9') return false; + for (unsigned int l = 0; l0?x/y:0; } - //! Compare the first \p l characters of two C-strings, ignoring the case. + //! Compare the first \p length characters of two C-strings, ignoring the case. /** \param str1 C-string. \param str2 C-string. - \param l Number of characters to compare. + \param length Number of characters to compare. \return \c 0 if the two strings are equal, something else otherwise. \note This function has to be defined since it is not provided by all C++-compilers (not ANSI). **/ - inline int strncasecmp(const char *const str1, const char *const str2, const int l) { - if (!l) return 0; + inline int strncasecmp(const char *const str1, const char *const str2, const int length) { + if (!length) return 0; if (!str1) return str2?-1:0; const char *nstr1 = str1, *nstr2 = str2; - int k, diff = 0; for (k = 0; k='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; *nd = (char)val; @@ -7021,13 +7062,13 @@ namespace cimg_library_suffixed { case 'x' : { char c = lowercase(*(++ns)); if ((c>='0' && c<='9') || (c>='a' && c<='f')) { - val = (c<='9'?c - '0':c - 'a' + 10); + val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10); c = lowercase(*(++ns)); if ((c>='0' && c<='9') || (c>='a' && c<='f')) { (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10); ++ns; } - *nd = val; + *nd = (char)val; } else *nd = c; } break; case 'u' : { // UTF-8 BMP @@ -7429,6 +7470,11 @@ namespace cimg_library_suffixed { // Get/set path to the \c wget binary. inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false); +#if cimg_OS==2 + // Get/set path to the \c powershell binary. + inline const char *powershell_path(const char *const user_path=0, const bool reinit_path=false); +#endif + //! Split filename into two C-strings \c body and \c extension. /** filename and body must not overlap! @@ -7534,7 +7580,7 @@ namespace cimg_library_suffixed { // Load file from network as a local temporary file. inline char *load_network(const char *const url, char *const filename_local, const unsigned int timeout=0, const bool try_fallback=false, - const char *const referer=0); + const char *const referer=0, const char *const user_agent=0); //! Return options specified on the command line. inline const char* option(const char *const name, const int argc, const char *const *const argv, @@ -7829,6 +7875,13 @@ namespace cimg_library_suffixed { cimg::t_bold, tmp, cimg::t_normal); +#if cimg_OS==2 + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::powershell_path()); + std::fprintf(cimg::output()," > Path of 'powershell_path': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); +#endif std::fprintf(cimg::output(),"\n"); delete[] tmp; @@ -8013,13 +8066,8 @@ namespace cimg_library_suffixed { } template - inline CImg<_cimg_Tfloat> invert(const CImg& instance, const bool use_LU=true) { - return instance.get_invert(use_LU); - } - - template - inline CImg<_cimg_Tfloat> pseudoinvert(const CImg& instance, const bool use_LU=false) { - return instance.get_pseudoinvert(use_LU); + inline CImg<_cimg_Tfloat> invert(const CImg& instance, const bool use_LU=false, const float lambda=0) { + return instance.get_invert(use_LU,lambda); } #define _cimg_create_pointwise_function(name) \ @@ -8030,6 +8078,7 @@ namespace cimg_library_suffixed { _cimg_create_pointwise_function(sqr) _cimg_create_pointwise_function(sqrt) + _cimg_create_pointwise_function(erf) _cimg_create_pointwise_function(exp) _cimg_create_pointwise_function(log) _cimg_create_pointwise_function(log2) @@ -8439,6 +8488,11 @@ namespace cimg_library_suffixed { return _is_closed; } + //! Return \c true if display is visible (i.e. not closed by the user), \c false otherwise. + bool is_visible() const { + return !is_closed(); + } + //! Return \c true if associated window has been resized on the screen, \c false otherwise. /** **/ @@ -10318,7 +10372,7 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; Display *const dpy = cimg::X11_attr().display; cimg_lock_display(); - static const char pix_data[8] = { 0 }; + static const char pix_data[8] = {}; XColor col; col.red = col.green = col.blue = 0; Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8); @@ -10579,7 +10633,7 @@ namespace cimg_library_suffixed { } } else { if (_normalization==3) { - if (cimg::type::is_float()) _min = (float)img.min_max(_max); + if (sizeof(T)>1 && cimg::type::string()!=cimg::type::string()) _min = (float)img.min_max(_max); else { _min = (float)cimg::type::min(); _max = (float)cimg::type::max(); } } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); const float delta = _max - _min, mm = 255/(delta?delta:1.f); @@ -11052,8 +11106,8 @@ namespace cimg_library_suffixed { const int border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2), border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1), - ww = disp->_width + 2*border1, - wh = disp->_height + border1 + border2, + ww = disp->width() + 2*border1, + wh = disp->height() + border1 + border2, sw = CImgDisplay::screen_width(), sh = CImgDisplay::screen_height(); int @@ -11064,7 +11118,7 @@ namespace cimg_library_suffixed { if (wx<0) wx = 0; if (wy<0) wy = 0; disp->_window = CreateWindowA("MDICLIENT",title?title:" ", - WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), + (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)), wx,wy,ww,wh,0,0,0,&(disp->_ccs)); if (!disp->_is_closed) { GetWindowRect(disp->_window,&rect); @@ -11076,10 +11130,10 @@ namespace cimg_library_suffixed { sx = (unsigned int)screen_width(), sy = (unsigned int)screen_height(); disp->_window = CreateWindowA("MDICLIENT",title?title:" ", - WS_POPUP | (disp->_is_closed?0:WS_VISIBLE), - (sx - disp->_width)/2, - (sy - disp->_height)/2, - disp->_width,disp->_height,0,0,0,&(disp->_ccs)); + (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)), + (int)(sx - disp->_width)/2, + (int)(sy - disp->_height)/2, + disp->width(),disp->height(),0,0,0,&(disp->_ccs)); disp->_window_x = disp->_window_y = 0; } SetForegroundWindow(disp->_window); @@ -11116,7 +11170,7 @@ namespace cimg_library_suffixed { _background_window = 0; if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0; else { - DEVMODE mode; +/* DEVMODE mode; unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; @@ -11132,13 +11186,15 @@ namespace cimg_library_suffixed { EnumDisplaySettings(0,ibest,&mode); ChangeDisplaySettings(&mode,0); } else _curr_mode.dmSize = 0; - +*/ + _curr_mode.dmSize = 0; const unsigned int sx = (unsigned int)screen_width(), sy = (unsigned int)screen_height(); if (sx!=_width || sy!=_height) { CLIENTCREATESTRUCT background_ccs = { 0,0 }; - _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs); + _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, + 0,0,(int)sx,(int)sy,0,0,0,&background_ccs); SetForegroundWindow(_background_window); } } @@ -11255,7 +11311,7 @@ namespace cimg_library_suffixed { CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); - if (is_empty()) return assign(nwidth,nheight); + if (is_empty()) return assign((unsigned int)nwidth,(unsigned int)nheight); const unsigned int tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100), tmpdimy = (nheight>0)?nheight:(-nheight*_height/100), @@ -11749,7 +11805,14 @@ namespace cimg_library_suffixed { if ((dy==1 || (siz*=dy)>osiz) && ((osiz = siz), dz==1 || (siz*=dz)>osiz) && ((osiz = siz), dc==1 || (siz*=dc)>osiz) && - ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) return siz; + ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) { + if (siz > cimg_max_buf_size){ + throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) exceeds maximum " + "allowed buffer size of %lu ", + pixel_type(),dx,dy,dz,dc,cimg_max_buf_size); + } + return siz; + } throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.", pixel_type(),dx,dy,dz,dc); } @@ -12150,7 +12213,7 @@ namespace cimg_library_suffixed { (e.g. already deallocated). \par Example \code - unsigned char tab[256*256] = { 0 }; + unsigned char tab[256*256] = {}; CImg img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab' img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab' tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1' @@ -12211,7 +12274,7 @@ namespace cimg_library_suffixed { const char *const axes_order):_data(0),_is_shared(false) { const size_t siz = safe_size(size_x,size_y,size_z,size_c); if (values && siz) { - unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = {}; for (unsigned int l = 0; axes_order[l]; ++l) { int c = cimg::lowercase(axes_order[l]); if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } @@ -13719,7 +13782,7 @@ namespace cimg_library_suffixed { template CImg& operator&=(const t value) { if (is_empty()) return *this; - cimg_openmp_for(*this,(ulongT)*ptr & (ulongT)value,32768); + cimg_openmp_for(*this,(longT)*ptr & (longT)value,32768); return *this; } @@ -13743,8 +13806,8 @@ namespace cimg_library_suffixed { T *ptrd = _data, *const ptre = _data + siz; if (siz>isiz) for (ulongT n = siz/isiz; n; --n) for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg& operator|=(const t value) { if (is_empty()) return *this; - cimg_openmp_for(*this,(ulongT)*ptr | (ulongT)value,32768); + cimg_openmp_for(*this,(longT)*ptr | (longT)value,32768); return *this; } @@ -13809,8 +13872,8 @@ namespace cimg_library_suffixed { T *ptrd = _data, *const ptre = _data + siz; if (siz>isiz) for (ulongT n = siz/isiz; n; --n) for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs CImg& operator^=(const t value) { if (is_empty()) return *this; - cimg_openmp_for(*this,(ulongT)*ptr ^ (ulongT)value,32768); + cimg_openmp_for(*this,(longT)*ptr ^ (longT)value,32768); return *this; } @@ -13881,8 +13944,8 @@ namespace cimg_library_suffixed { T *ptrd = _data, *const ptre = _data + siz; if (siz>isiz) for (ulongT n = siz/isiz; n; --n) for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs"unknown" is returned. **/ static const char* pixel_type() { @@ -16406,7 +16469,8 @@ namespace cimg_library_suffixed { \param colors List of colors of the 3D object. \param opacities List (or image) of opacities of the 3D object. \param full_check Tells if full checking of the 3D object must be performed. - \param[out] error_message C-string to contain the error message, if the test does not succeed. + \param[out] error_message C-string to contain the error message, if the test does not succeed + (at least 256 bytes). \note - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of each 3D object component is checked. @@ -16423,7 +16487,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) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) defines no vertices but %u primitives, " "%u colors and %lu opacities", _width,primitives._width,primitives._width, @@ -16435,19 +16499,19 @@ namespace cimg_library_suffixed { // Check consistency of vertices. if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)", _width,primitives._width,_width,_height,_depth,_spectrum); return false; } if (colors._width>primitives._width + 1) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) defines %u colors", _width,primitives._width,colors._width); return false; } if (opacities.size()>primitives._width) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) defines %lu opacities", _width,primitives._width,(unsigned long)opacities.size()); return false; @@ -16462,7 +16526,7 @@ namespace cimg_library_suffixed { case 1 : { // Point const unsigned int i0 = (unsigned int)primitive(0); if (i0>=_width) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) refers to invalid vertex index %u in " "point primitive [%u]", _width,primitives._width,i0,l); @@ -16474,7 +16538,7 @@ namespace cimg_library_suffixed { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " "sphere primitive [%u]", _width,primitives._width,i0,i1,l); @@ -16486,7 +16550,7 @@ namespace cimg_library_suffixed { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " "segment primitive [%u]", _width,primitives._width,i0,i1,l); @@ -16499,7 +16563,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) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " "triangle primitive [%u]", _width,primitives._width,i0,i1,i2,l); @@ -16513,7 +16577,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) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "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); @@ -16521,7 +16585,7 @@ namespace cimg_library_suffixed { } } break; default : - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) defines an invalid primitive [%u] of size %u", _width,primitives._width,l,(unsigned int)psiz); return false; @@ -16532,7 +16596,7 @@ namespace cimg_library_suffixed { cimglist_for(colors,c) { const CImg& color = colors[c]; if (!color) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) defines no color for primitive [%u]", _width,primitives._width,c); return false; @@ -16543,7 +16607,7 @@ namespace cimg_library_suffixed { if (colors._width>primitives._width) { const CImg &light = colors.back(); if (!light || light._depth>1) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)", _width,primitives._width,light._width, light._height,light._depth,light._spectrum); @@ -16562,14 +16626,14 @@ namespace cimg_library_suffixed { \note - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of each 3D object component is checked. - - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + - Size of the string \c error_message should be at least 256-bytes long, to be able to contain the error message. **/ bool is_CImg3d(const bool full_check=true, char *const error_message=0) const { if (error_message) *error_message = 0; // Check instance dimension and header. if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d has invalid dimensions (%u,%u,%u,%u)", _width,_height,_depth,_spectrum); return false; @@ -16577,7 +16641,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) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d header not found"); return false; } @@ -16589,7 +16653,7 @@ namespace cimg_library_suffixed { if (!full_check) { const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives; if (_data + minimal_size>ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size); return false; @@ -16599,13 +16663,13 @@ namespace cimg_library_suffixed { // Check consistency of vertex data. if (!nb_points) { if (nb_primitives) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) defines no vertices but %u primitives", nb_points,nb_primitives,nb_primitives); return false; } if (ptrs!=ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "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":""); @@ -16614,7 +16678,7 @@ namespace cimg_library_suffixed { return true; } if (ptrs + 3*nb_points>ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) defines only %u vertices data", nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); return false; @@ -16623,7 +16687,7 @@ namespace cimg_library_suffixed { // Check consistency of primitive data. if (ptrs==ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) defines %u vertices but no primitive", nb_points,nb_primitives,nb_points); return false; @@ -16637,7 +16701,7 @@ namespace cimg_library_suffixed { case 1 : { // Point const unsigned int i0 = cimg::float2uint((float)*(ptrs++)); if (i0>=nb_points) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]", nb_points,nb_primitives,i0,p); return false; @@ -16649,7 +16713,7 @@ namespace cimg_library_suffixed { i1 = cimg::float2uint((float)*(ptrs++)); ptrs+=3; if (i0>=nb_points || i1>=nb_points) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " "sphere primitive [%u]", nb_points,nb_primitives,i0,i1,p); @@ -16662,7 +16726,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) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " "segment primitive [%u]", nb_points,nb_primitives,i0,i1,p); @@ -16676,7 +16740,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) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " "triangle primitive [%u]", nb_points,nb_primitives,i0,i1,i2,p); @@ -16691,7 +16755,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) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "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); @@ -16699,13 +16763,13 @@ namespace cimg_library_suffixed { } } break; default : - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", nb_points,nb_primitives,p,nb_inds); return false; } if (ptrs>ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " "%u values missing", nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); @@ -16715,7 +16779,7 @@ namespace cimg_library_suffixed { // Check consistency of color data. if (ptrs==ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) defines no color/texture data", nb_points,nb_primitives); return false; @@ -16729,7 +16793,7 @@ namespace cimg_library_suffixed { s = (unsigned int)*(ptrs - 1); if (!h && !s) { if (w>=c) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u " "for primitive [%u]", nb_points,nb_primitives,w,c); @@ -16738,7 +16802,7 @@ namespace cimg_library_suffixed { } else ptrs+=w*h*s; } if (ptrs>ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " "%u values missing", nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); @@ -16748,7 +16812,7 @@ namespace cimg_library_suffixed { // Check consistency of opacity data. if (ptrs==ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) defines no opacity data", nb_points,nb_primitives); return false; @@ -16761,7 +16825,7 @@ namespace cimg_library_suffixed { s = (unsigned int)*(ptrs - 1); if (!h && !s) { if (w>=o) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) refers to invalid shared opacity index %u " "for primitive [%u]", nb_points,nb_primitives,w,o); @@ -16770,7 +16834,7 @@ namespace cimg_library_suffixed { } else ptrs+=w*h*s; } if (ptrs>ptre) { - if (error_message) cimg_sprintf(error_message, + if (error_message) cimg_snprintf(error_message,256, "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", nb_points,nb_primitives,o); return false; @@ -16779,7 +16843,7 @@ namespace cimg_library_suffixed { // Check end of data. if (ptrs1?"s":""); return false; @@ -16810,9 +16874,8 @@ namespace cimg_library_suffixed { CImg expr, pexpr; const CImg& imgin; - const CImgList& listin; CImg &imgout; - CImgList& listout; + CImgList& imglist; CImg _img_stats, &img_stats, constcache_vals; CImgList _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm; @@ -16820,34 +16883,33 @@ namespace cimg_library_suffixed { CImg level, variable_pos, reserved_label; CImgList variable_def, macro_def, macro_body; - CImgList macro_body_is_string; char *user_macro; unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type, constcache_size; - bool is_parallelizable, is_end_code, is_fill, need_input_copy; + bool is_parallelizable, is_noncritical_run, is_end_code, is_fill, return_new_comp, need_input_copy; double *result; cimg_uint64 rng; const char *const calling_function, *s_op, *ss_op; typedef double (*mp_func)(_cimg_math_parser&); -#define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value? #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value? -#define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value? -#define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable? +#define _cimg_mp_is_const_scalar(arg) (memtype[arg]==1) // Is const scalar? #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? +#define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value? +#define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)? #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN) #define _cimg_mp_calling_function s_calling_function()._data #define _cimg_mp_op(s) s_op = s; ss_op = ss #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char) -#define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char) -#define _cimg_mp_check_constant_index(arg) check_constant_index(arg,ss,se,saved_char) +#define _cimg_mp_check_const_scalar(arg,n_arg,mode) check_const_scalar(arg,n_arg,mode,ss,se,saved_char) +#define _cimg_mp_check_const_index(arg) check_const_index(arg,ss,se,saved_char) #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char) -#define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char) +#define _cimg_mp_check_list() check_list(ss,se,saved_char) #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; } #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan) -#define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val))) +#define _cimg_mp_const_scalar(val) _cimg_mp_return(const_scalar((double)(val))) #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2)) @@ -16861,6 +16923,9 @@ namespace cimg_library_suffixed { #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2)) #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2)) #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3)) +#define _cimg_mp_vector4_vvss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_vvss(op,i1,i2,i3,i4)) +#define _cimg_mp_vector4_vsss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_vsss(op,i1,i2,i3,i4)) +#define _cimg_mp_vector4_svss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_svss(op,i1,i2,i3,i4)) #define _cimg_mp_strerr \ *se = saved_char; \ for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \ @@ -16875,15 +16940,13 @@ namespace cimg_library_suffixed { _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_inputs=0, CImgList *const list_outputs=0, - const bool _is_fill=false): + CImgList *const list_images=0, const bool _is_fill=false): code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), - p_break((CImg*)(cimg_ulong)-2), - imgin(img_input),listin(list_inputs?*list_inputs:CImgList::const_empty()), - imgout(img_output?*img_output:CImg::empty()),listout(list_outputs?*list_outputs:CImgList::empty()), + p_break((CImg*)(cimg_ulong)-2),imgin(img_input), + imgout(img_output?*img_output:CImg::empty()),imglist(list_images?*list_images:CImgList::empty()), img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0), mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0), - constcache_size(0),is_parallelizable(true),is_fill(_is_fill),need_input_copy(false), + constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(_is_fill),need_input_copy(false), rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") { #if cimg_use_openmp!=0 @@ -16911,8 +16974,8 @@ namespace cimg_library_suffixed { level = get_level(expr); // Init constant values. -#define _cimg_mp_interpolation (reserved_label[30]!=~0U?reserved_label[30]:0) -#define _cimg_mp_boundary (reserved_label[31]!=~0U?reserved_label[31]:0) +#define _cimg_mp_interpolation (reserved_label[31]!=~0U?reserved_label[31]:0) +#define _cimg_mp_boundary (reserved_label[32]!=~0U?reserved_label[32]:0) #define _cimg_mp_slot_t 17 #define _cimg_mp_slot_nan 29 #define _cimg_mp_slot_x 30 @@ -16933,31 +16996,31 @@ namespace cimg_library_suffixed { mem[23] = (double)imgin._width*imgin._height; // wh mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds - mem[26] = (double)listin._width; // l + mem[26] = (double)imglist._width; // l mem[27] = std::exp(1.); // e mem[28] = cimg::PI; // pi mem[_cimg_mp_slot_nan] = cimg::type::nan(); // nan // Set value property : - // { -2 = other | -1 = variable | 0 = computation value | + // { -1 = reserved (e.g. variable) | 0 = computation value | // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }. memtype.assign(mem._width,1,1,1,0); for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1; memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = - memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -2; + memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -1; mempos = _cimg_mp_slot_c + 1; variable_pos.assign(8); reserved_label.assign(128,1,1,1,~0U); - // reserved_label[0-31] are used to store the memory index of these variables: - // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, - // [8] = is, [9] = ip, [10] = ic, [11] = in, [12] = xm, [13] = ym, [14] = zm, [15] = cm, [16] = xM, - // [17] = yM, [18] = zM, [19] = cM, [20] = i0...[29] = i9, [30] = interpolation, [31] = boundary + // reserved_label[0-32] are used to store the memory index of these variables: + // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, [8] = id, + // [9] = is, [10] = ip, [11] = ic, [12] = in, [13] = xm, [14] = ym, [15] = zm, [16] = cm, [17] = xM, + // [18] = yM, [19] = zM, [20] = cM, [21] = i0...[30] = i9, [31] = interpolation, [32] = boundary // Compile expression into a sequence of opcodes. s_op = ""; ss_op = expr._data; - const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false); - if (!_cimg_mp_is_constant(ind_result)) { + const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,0); + if (!_cimg_mp_is_const_scalar(ind_result)) { if (_cimg_mp_is_vector(ind_result)) CImg(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true). fill(cimg::type::nan()); @@ -16979,7 +17042,7 @@ namespace cimg_library_suffixed { opcode.assign(); opcode._is_shared = true; - // Execute begin() bloc if any specified. + // Execute begin() block if any specified. if (code_begin) { mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; p_code_end = code_begin.end(); @@ -16995,10 +17058,9 @@ namespace cimg_library_suffixed { _cimg_math_parser(): code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), p_code_end(0),p_break((CImg*)(cimg_ulong)-2), - imgin(CImg::const_empty()),listin(CImgList::const_empty()), - imgout(CImg::empty()),listout(CImgList::empty()), + imgin(CImg::const_empty()),imgout(CImg::empty()),imglist(CImgList::empty()), img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0), - result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false), + result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(false), need_input_copy(false),rng(0),calling_function(0) { mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() result = mem._data; @@ -17007,14 +17069,15 @@ namespace cimg_library_suffixed { _cimg_math_parser(const _cimg_math_parser& mp): mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t), p_code_end(mp.p_code_end),p_break(mp.p_break), - imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout), + imgin(mp.imgin),imgout(mp.imgout),imglist(mp.imglist), img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm), debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0), - is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill),need_input_copy(mp.need_input_copy), - result(mem._data + (mp.result - mp.mem._data)),rng((cimg::_rand(),cimg::rng())),calling_function(0) { + is_parallelizable(mp.is_parallelizable),is_noncritical_run(mp.is_noncritical_run),is_fill(mp.is_fill), + need_input_copy(mp.need_input_copy),result(mem._data + (mp.result - mp.mem._data)), + rng((cimg::_rand(),cimg::rng())),calling_function(0) { #if cimg_use_openmp!=0 - mem[_cimg_mp_slot_t] = omp_get_thread_num(); + mem[_cimg_mp_slot_t] = (double)omp_get_thread_num(); rng+=omp_get_thread_num(); #endif opcode.assign(); @@ -17023,16 +17086,14 @@ namespace cimg_library_suffixed { // Compilation procedure. unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref, - const bool is_critical) { + const unsigned char block_flags) { if (depth>256) { cimg::strellipsize(expr,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Call stack overflow (infinite recursion?), " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function, - (ss - 4)>expr._data?"...":"", - (ss - 4)>expr._data?ss - 4:expr._data, - se<&expr.back()?"...":""); + (ss - 4)>expr._data?ss - 4:expr._data); } char c1, c2; @@ -17040,23 +17101,29 @@ namespace cimg_library_suffixed { do { c2 = 0; if (ssss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; + while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss; // Remove leading blanks and ';' + while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; // Remove trailing blanks and ';' } - while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { + while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Remove useless start/end parentheses ++ss; --se; c2 = 1; } + if (*ss=='_' && ss + 1=se) return _cimg_mp_slot_nan; + c2 = 1; + } } while (c2 && ss::%s: %s%s Missing %s, in expression '%s%s%s'.", + "CImg<%s>::%s: %s%s Missing %s, in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", *s_op=='F'?"argument":"item", - (ss_op - 4)>expr._data?"...":"", - (ss_op - 4)>expr._data?ss_op - 4:expr._data, - ss_op + std::strlen(ss_op)<&expr.back()?"...":""); + ss_op); } static const size_t siz_ref = 7*sizeof(unsigned int); @@ -17070,11 +17137,16 @@ namespace cimg_library_suffixed { *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0; double val = 0, val1, val2; mp_func op; + return_new_comp = false; + + // Bits of 'block_flags' tell about in which code block we currently are: + // 0: critical(), 1: begin(), 2: begin_t(), 3: end(), 4: end_t(). + const bool is_inside_critical = (bool)(block_flags&1); // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value // linked to the returned memory slot (reference that cannot be determined at compile time). // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) | - // 3 = image value (coordinates) | 4 = image value as a vector (offsets) | + // 3 = image value (coordinates) | 4 = image value as a vector (offset) | // 5 = image value as a vector (coordinates) }. // Depending on p_ref[0], the remaining p_ref[k] have the following meaning: // When p_ref[0]==0, p_ref is actually unlinked. @@ -17109,8 +17181,8 @@ namespace cimg_library_suffixed { } } if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); - if (nb==1) _cimg_mp_constant(val); - if (nb==2 && sep=='%') _cimg_mp_constant(val/100); + if (nb==1) _cimg_mp_const_scalar(val); + if (nb==2 && sep=='%') _cimg_mp_const_scalar(val/100); if (ss1==se) switch (*ss) { // One-char reserved variable case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c); @@ -17126,6 +17198,13 @@ namespace cimg_library_suffixed { case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22); case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21); case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t); + case 'n' : + if (reserved_label[(int)'n']!=~0U) _cimg_mp_return(reserved_label[(int)'n']); +#if cimg_use_openmp!=0 + _cimg_mp_const_scalar((double)omp_get_max_threads()); +#else + _cimg_mp_return(1); +#endif case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18); case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x); case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y); @@ -17133,6 +17212,9 @@ namespace cimg_library_suffixed { case 'u' : if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']); _cimg_mp_scalar2(mp_u,0,1); + case 'v' : + if (reserved_label[(int)'v']!=~0U) _cimg_mp_return(reserved_label[(int)'v']); + _cimg_mp_scalar2(mp_u,11,1); case 'g' : if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']); _cimg_mp_scalar0(mp_g); @@ -17146,6 +17228,7 @@ namespace cimg_library_suffixed { need_input_copy = true; pos = vector(imgin._spectrum); CImg::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); case 'R' : if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']); @@ -17172,40 +17255,41 @@ namespace cimg_library_suffixed { _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28); if (*ss=='i') { if (*ss1>='0' && *ss1<='9') { // i0...i9 - pos = 20 + *ss1 - '0'; + pos = 21 + *ss1 - '0'; if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); need_input_copy = true; - _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 20,0,0); + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 21,0,0); } switch (*ss1) { case 'm' : arg1 = 4; arg2 = 0; break; // im case 'M' : arg1 = 5; arg2 = 1; break; // iM case 'a' : arg1 = 6; arg2 = 2; break; // ia case 'v' : arg1 = 7; arg2 = 3; break; // iv - case 's' : arg1 = 8; arg2 = 12; break; // is - case 'p' : arg1 = 9; arg2 = 13; break; // ip + case 'd' : arg1 = 8; arg2 = 3; break; // id + case 's' : arg1 = 9; arg2 = 12; break; // is + case 'p' : arg1 = 10; arg2 = 13; break; // ip case 'c' : // ic - if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); - if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; + if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); + if (mem_img_median==~0U) mem_img_median = imgin?const_scalar(imgin.median()):0; _cimg_mp_return(mem_img_median); break; case 'n' : // in - if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); - if (mem_img_norm==~0U) mem_img_norm = imgin?constant(imgin.magnitude()):0; + if (reserved_label[12]!=~0U) _cimg_mp_return(reserved_label[12]); + if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude()):0; _cimg_mp_return(mem_img_norm); } } else if (*ss1=='m') switch (*ss) { - case 'x' : arg1 = 12; arg2 = 4; break; // xm - case 'y' : arg1 = 13; arg2 = 5; break; // ym - case 'z' : arg1 = 14; arg2 = 6; break; // zm - case 'c' : arg1 = 15; arg2 = 7; break; // cm + case 'x' : arg1 = 13; arg2 = 4; break; // xm + case 'y' : arg1 = 14; arg2 = 5; break; // ym + case 'z' : arg1 = 15; arg2 = 6; break; // zm + case 'c' : arg1 = 16; arg2 = 7; break; // cm } else if (*ss1=='M') switch (*ss) { - case 'x' : arg1 = 16; arg2 = 8; break; // xM - case 'y' : arg1 = 17; arg2 = 9; break; // yM - case 'z' : arg1 = 18; arg2 = 10; break; // zM - case 'c' : arg1 = 19; arg2 = 11; break; // cM + case 'x' : arg1 = 17; arg2 = 8; break; // xM + case 'y' : arg1 = 18; arg2 = 9; break; // yM + case 'z' : arg1 = 19; arg2 = 10; break; // zM + case 'c' : arg1 = 20; arg2 = 11; break; // cM } if (arg1!=~0U) { if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); @@ -17213,7 +17297,8 @@ namespace cimg_library_suffixed { 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]); + if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = const_scalar(img_stats[arg2]); + if (arg1==8) _cimg_mp_const_scalar(std::sqrt(img_stats[arg2])); // id: std variation _cimg_mp_return(mem_img_stats[arg2]); } } else if (ss3==se) { // Three-chars reserved variable @@ -17226,10 +17311,11 @@ namespace cimg_library_suffixed { pos = ~0U; is_sth = false; + for (s0 = ss, s = ss1; s='i'?1:3,0); if (_cimg_mp_is_vector(arg2)) { if (p1!=~0U) { - _cimg_mp_check_constant_index(p1); - p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - p2 = listin[p3]._spectrum; + _cimg_mp_check_const_index(p1); + p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width()); + p2 = imglist[p3]._spectrum; } else p2 = imgin._spectrum; if (!p2) _cimg_mp_return(0); _cimg_mp_check_type(arg2,2,2,p2); } else p2 = 0; if (p_ref) { - *p_ref = _cimg_mp_is_vector(arg2)?4:2; + *p_ref = *ss=='I' || *ss=='J'?4:2; p_ref[1] = p1; p_ref[2] = (unsigned int)is_relative; p_ref[3] = arg1; if (_cimg_mp_is_vector(arg2)) - set_variable_vector(arg2); // Prevent from being used in further optimization - else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; - if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2; + set_reserved_vector(arg2); // Prevent from being used in further optimization + else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; + if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1; } if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); + if (!imglist) _cimg_mp_return(arg2); if (*ss>='i') CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), arg2,p1,arg1).move_to(code); @@ -17318,21 +17404,21 @@ namespace cimg_library_suffixed { } if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; if (*ss2=='#') { // Index specified s0 = ss3; while (s0='i'?1:3,0); if (s0='i') CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), arg5,p1,arg1,arg2,arg3,arg4).move_to(code); @@ -17412,29 +17498,25 @@ namespace cimg_library_suffixed { // Assign vector value (direct). if (l_variable_name>3 && *ve1==']' && *ss!='[') { s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; - is_sth = true; // is_valid_variable_name? - if (*ss>='0' && *ss<='9') is_sth = false; - else for (ns = ss; nsss) { + if (s0>ss && cimg::is_varname(ss,s0 - ss)) { variable_name[s0 - ss] = 0; // Remove brackets in variable name get_variable_pos(variable_name,arg1,arg2); arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot if (arg1==~0U || _cimg_mp_is_scalar(arg1)) - compile(ss,s0 - 1,depth1,0,is_critical); // Variable does not exist or is not a vector -> error + compile(ss,s0,depth1,0,block_flags); // Variable does not exist or is not a vector -> error - arg2 = compile(++s0,ve1,depth1,0,is_critical); // Index - arg3 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + arg2 = compile(++s0,ve1,depth1,0,block_flags); // Index + arg3 = compile(s + 1,se,depth1,0,block_flags); // Value to assign _cimg_mp_check_type(arg3,2,1,0); - if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly + if (_cimg_mp_is_const_scalar(arg2)) { // Constant index -> return corresponding variable slot directly nb = (int)mem[arg2]; if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) { arg1+=nb + 1; CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); _cimg_mp_return(arg1); } - compile(ss,s,depth1,0,is_critical); // Out-of-bounds reference -> error + compile(ss,s,depth1,0,block_flags); // Out-of-bounds reference -> error } // Case of non-constant index -> return assigned value + linked reference @@ -17442,8 +17524,8 @@ namespace cimg_library_suffixed { *p_ref = 1; p_ref[1] = arg1; p_ref[2] = arg2; - if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; // Prevent from being used in further optimization - if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; + if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; } CImg::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2). move_to(code); @@ -17454,13 +17536,8 @@ namespace cimg_library_suffixed { // Assign user-defined macro. if (l_variable_name>2 && *ve1==')' && *ss!='(') { s0 = ve1; while (s0>ss && *s0!='(') --s0; - is_sth = std::strncmp(variable_name,"debug(",6) && - std::strncmp(variable_name,"print(",6); // is_valid_function_name? - if (*ss>='0' && *ss<='9') is_sth = false; - else for (ns = ss; nsss) { // Looks like a valid function declaration + if (cimg::is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) && + std::strncmp(variable_name,"print(",6)) { // Valid macro name s0 = variable_name._data + (s0 - ss); *s0 = 0; s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis @@ -17468,45 +17545,56 @@ namespace cimg_library_suffixed { ++s; while (*s && cimg::is_blank(*s)) ++s; CImg(s,(unsigned int)(se - s + 1)).move_to(macro_body,0); + bool is_variadic = false; p1 = 1; // Index of current parsed argument for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments + if (is_variadic && p1>1) { + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Multiple arguments not allowed when first one is " + "variadic, in macro definition '%s()', in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data,s0); + } if (p1>24) { _cimg_mp_strerr; cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro " - "definition '%s()', in expression '%s%s%s'.", + "CImg<%s>::%s: %s: Too much specified arguments (>24), in macro " + "definition '%s()', in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + variable_name._data,s0); } while (*s && cimg::is_blank(*s)) ++s; if (*s==')' && p1==1) break; // Function has no arguments - s2 = s; // Start of the argument name is_sth = true; // is_valid_argument_name? - if (*s>='0' && *s<='9') is_sth = false; - else for (ns = s; ns='0' && *s2<='9') is_sth = false; + else for (ns = s2; nss2 && *ns=='.' && ns[1]=='.' && ns[2]=='.') { is_variadic = true; ns+=3; } + else if (*ns=='.') is_sth = false; while (*ns && cimg::is_blank(*ns)) ++ns; + if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) { _cimg_mp_strerr; cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: %s name specified for argument %u when defining " - "macro '%s()', in expression '%s%s%s'.", + "macro '%s()', in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, is_sth?"Empty":"Invalid",p1, - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + variable_name._data,s0); } - if (ns==s1 || *ns==',') { // New argument found + + if (ns==s1 || *ns==',' || (is_variadic && *ns=='.')) { // New argument found *s3 = 0; p2 = (unsigned int)(s3 - s2); // Argument length for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number - if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) || - (ps + p2macro_body[0]._data && cimg::is_varchar(*(ps - 1))) || + (ps + p2macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign *(ps - 1) = (char)p1; if (ps + p26 && !std::strncmp(variable_name,"const ",6); - s0 = variable_name._data; if (is_const) { s0+=6; while (cimg::is_blank(*s0)) ++s0; variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1); } + if (cimg::is_varname(variable_name)) { // Valid variable name - if (*variable_name>='0' && *variable_name<='9') is_sth = false; - else for (ns = variable_name._data; *ns; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - - // Assign variable (direct). - if (is_sth) { + // Assign variable (direct). get_variable_pos(variable_name,arg1,arg2); - arg3 = compile(s + 1,se,depth1,0,is_critical); - if (is_const) _cimg_mp_check_constant(arg3,2,0); + arg3 = compile(s + 1,se,depth1,0,block_flags); + is_sth = return_new_comp; // is arg3 a new blank object? + if (is_const) _cimg_mp_check_const_scalar(arg3,2,0); arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; if (arg1==~0U) { // Create new variable if (_cimg_mp_is_vector(arg3)) { // Vector variable - arg1 = is_comp_vector(arg3)?arg3:vector_copy(arg3); - set_variable_vector(arg1); // Prevent from being used in further optimization + arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3); + set_reserved_vector(arg1); // Prevent from being used in further optimization } else { // Scalar variable if (is_const) arg1 = arg3; else { - arg1 = _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3); + arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3); memtype[arg1] = -1; } } + if (arg2!=~0U) reserved_label[arg2] = arg1; - else { - if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); - variable_pos[variable_def._width] = arg1; - variable_name.move_to(variable_def); - } + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg1; + variable_name.move_to(variable_def); + } } else { // Variable already exists -> assign a new value - if (is_const || _cimg_mp_is_constant(arg1)) { + if (is_const || _cimg_mp_is_const_scalar(arg1)) { _cimg_mp_strerr; cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - _cimg_mp_is_constant(arg1)?"already-defined ":"non-", + _cimg_mp_is_const_scalar(arg1)?"":"non-", variable_name._data, - !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + !_cimg_mp_is_const_scalar(arg1) && is_const?" as a const variable":"", + s0); } _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1)) { // Vector @@ -17608,18 +17693,19 @@ namespace cimg_library_suffixed { } else // Scalar CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); } + return_new_comp = false; _cimg_mp_return(arg1); } // Assign lvalue (variable name was not valid for a direct assignment). arg1 = ~0U; is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? - if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment + if (is_sth) break; // Do nothing and make ternary operator priority over assignment if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { ref.assign(7); - arg1 = compile(ss,s,depth1,ref,is_critical); // Lvalue slot - arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + arg1 = compile(ss,s,depth1,ref,block_flags); // Lvalue slot + arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to assign if (*ref==1) { // Vector value (scalar): V[k] = scalar _cimg_mp_check_type(arg2,2,1,0); @@ -17632,14 +17718,14 @@ namespace cimg_library_suffixed { } if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); + if (!imglist) _cimg_mp_return(arg2); CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), arg2,p1,arg3).move_to(code); } else { @@ -17651,7 +17737,7 @@ namespace cimg_library_suffixed { } if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -17661,7 +17747,7 @@ namespace cimg_library_suffixed { arg6 = ref[6]; // C if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); + if (!imglist) _cimg_mp_return(arg2); CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), arg2,p1,arg3,arg4,arg5,arg6).move_to(code); } else { @@ -17673,19 +17759,19 @@ namespace cimg_library_suffixed { } if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); + if (!imglist) _cimg_mp_return(arg2); if (_cimg_mp_is_scalar(arg2)) CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), arg2,p1,arg3).move_to(code); else { - _cimg_mp_check_constant_index(p1); + _cimg_mp_check_const_index(p1); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code); } @@ -17703,7 +17789,7 @@ namespace cimg_library_suffixed { } if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -17712,12 +17798,12 @@ namespace cimg_library_suffixed { arg5 = ref[5]; // Z if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg2); + if (!imglist) _cimg_mp_return(arg2); if (_cimg_mp_is_scalar(arg2)) CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), arg2,p1,arg3,arg4,arg5).move_to(code); else { - _cimg_mp_check_constant_index(p1); + _cimg_mp_check_const_index(p1); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); } @@ -17745,7 +17831,7 @@ namespace cimg_library_suffixed { _cimg_mp_return(arg1); } - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar _cimg_mp_check_type(arg2,2,1,0); CImg::vector((ulongT)mp_copy,arg1,arg2).move_to(code); _cimg_mp_return(arg1); @@ -17757,11 +17843,10 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid %slvalue '%s', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"", - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + arg1!=~0U && _cimg_mp_is_const_scalar(arg1)?"const ":"", + variable_name._data,s0); } // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++. @@ -17771,8 +17856,8 @@ namespace cimg_library_suffixed { _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"); ref.assign(7); - arg1 = compile(ss,ns,depth1,ref,is_critical); // Vector slot - arg2 = compile(s + 1,se,depth1,0,is_critical); // Right operand + arg1 = compile(ss,ns,depth1,ref,block_flags); // Vector slot + arg2 = compile(s + 1,se,depth1,0,block_flags); // Right operand _cimg_mp_check_type(arg1,1,2,2); _cimg_mp_check_type(arg2,2,3,2); if (_cimg_mp_is_vector(arg2)) { // Complex **= complex @@ -17796,14 +17881,14 @@ namespace cimg_library_suffixed { } if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - _cimg_mp_check_constant_index(p1); + if (!imglist) _cimg_mp_return(arg1); + _cimg_mp_check_const_index(p1); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); } else { @@ -17813,7 +17898,7 @@ namespace cimg_library_suffixed { } } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -17821,8 +17906,8 @@ namespace cimg_library_suffixed { arg5 = ref[5]; // Z if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); - _cimg_mp_check_constant_index(p1); + if (!imglist) _cimg_mp_return(arg1); + _cimg_mp_check_const_index(p1); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } else { @@ -17836,7 +17921,7 @@ namespace cimg_library_suffixed { } for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 - if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' || + if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || (*ps=='%' && s[1]!='=') || *ps=='&' || *ps=='^' || *ps=='|' || (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=) @@ -17855,15 +17940,15 @@ namespace cimg_library_suffixed { s1 = *ps=='>' || *ps=='<'?ns:ps; ref.assign(7); - arg1 = compile(ss,s1,depth1,ref,is_critical); // Variable slot - arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to apply + arg1 = compile(ss,s1,depth1,ref,block_flags); // Variable slot + arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to apply // Check for particular case to be simplified. if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1); if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1); // Apply operator on a copy to prevent modifying a constant or a variable. - if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) { + if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); else arg1 = scalar1(mp_copy,arg1); } @@ -17880,7 +17965,7 @@ namespace cimg_library_suffixed { } if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -17888,7 +17973,7 @@ namespace cimg_library_suffixed { if (p_ref) std::memcpy(p_ref,ref,siz_ref); CImg::vector((ulongT)op,arg1,arg2).move_to(code); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); + if (!imglist) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), arg1,p1,arg3).move_to(code); } else { @@ -17900,7 +17985,7 @@ namespace cimg_library_suffixed { } if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -17911,7 +17996,7 @@ namespace cimg_library_suffixed { if (p_ref) std::memcpy(p_ref,ref,siz_ref); CImg::vector((ulongT)op,arg1,arg2).move_to(code); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); + if (!imglist) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), arg1,p1,arg3,arg4,arg5,arg6).move_to(code); } else { @@ -17923,7 +18008,7 @@ namespace cimg_library_suffixed { } if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -17931,7 +18016,7 @@ namespace cimg_library_suffixed { if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); + if (!imglist) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); } else { @@ -17943,7 +18028,7 @@ namespace cimg_library_suffixed { } if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -17953,7 +18038,7 @@ namespace cimg_library_suffixed { if (p_ref) std::memcpy(p_ref,ref,siz_ref); if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); if (p1!=~0U) { - if (!listout) _cimg_mp_return(arg1); + if (!imglist) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } else { @@ -17971,7 +18056,7 @@ namespace cimg_library_suffixed { _cimg_mp_return(arg1); } - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar _cimg_mp_check_type(arg2,2,1,0); CImg::vector((ulongT)op,arg1,arg2).move_to(code); _cimg_mp_return(arg1); @@ -17983,77 +18068,79 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid %slvalue '%s', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - _cimg_mp_is_constant(arg1)?"const ":"", - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + _cimg_mp_is_const_scalar(arg1)?"const ":"", + variable_name._data,s0); } for (s = ss1; s::vector((ulongT)mp_if,pos,arg1,arg2,arg3, p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; _cimg_mp_return(pos); } for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') _cimg_mp_op("Operator '||'"); - arg1 = compile(ss,s,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); _cimg_mp_check_type(arg1,1,1,0); if (arg1>0 && arg1<=16) _cimg_mp_return(1); p2 = code._width; - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(mem[arg1] || mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1] || mem[arg2]); if (!arg1) _cimg_mp_return(arg2); pos = scalar(); CImg::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2). move_to(code,p2); + return_new_comp = true; _cimg_mp_return(pos); } for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') _cimg_mp_op("Operator '&&'"); - arg1 = compile(ss,s,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); _cimg_mp_check_type(arg1,1,1,0); if (!arg1) _cimg_mp_return(0); p2 = code._width; - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,1,0); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(mem[arg1] && mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1] && mem[arg2]); if (arg1>0 && arg1<=16) _cimg_mp_return(arg2); pos = scalar(); CImg::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2). move_to(code,p2); + return_new_comp = true; _cimg_mp_return(pos); } for (s = se2; s>ss; --s) if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') _cimg_mp_op("Operator '|'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { @@ -18064,8 +18151,8 @@ namespace cimg_library_suffixed { if (!arg1) _cimg_mp_return(arg2); _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2); } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar((longT)mem[arg1] | (longT)mem[arg2]); if (!arg2) _cimg_mp_return(arg1); if (!arg1) _cimg_mp_return(arg2); _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2); @@ -18074,14 +18161,14 @@ namespace cimg_library_suffixed { for (s = se2; s>ss; --s) if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') _cimg_mp_op("Operator '&'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar((longT)mem[arg1] & (longT)mem[arg2]); if (!arg1 || !arg2) _cimg_mp_return(0); _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2); } @@ -18089,8 +18176,8 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') _cimg_mp_op("Operator '!='"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); if (arg1==arg2) _cimg_mp_return(0); p1 = _cimg_mp_size(arg1); p2 = _cimg_mp_size(arg2); @@ -18098,17 +18185,19 @@ namespace cimg_library_suffixed { if (p1 && p2 && p1!=p2) _cimg_mp_return(1); pos = scalar(); CImg::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]!=mem[arg2]); _cimg_mp_scalar2(mp_neq,arg1,arg2); } for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') _cimg_mp_op("Operator '=='"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); if (arg1==arg2) _cimg_mp_return(1); p1 = _cimg_mp_size(arg1); p2 = _cimg_mp_size(arg2); @@ -18116,22 +18205,25 @@ namespace cimg_library_suffixed { if (p1 && p2 && p1!=p2) _cimg_mp_return(0); pos = scalar(); CImg::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]==mem[arg2]); _cimg_mp_scalar2(mp_eq,arg1,arg2); } for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') _cimg_mp_op("Operator '<='"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]<=mem[arg2]); if (arg1==arg2) _cimg_mp_return(1); _cimg_mp_scalar2(mp_lte,arg1,arg2); } @@ -18139,13 +18231,14 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') _cimg_mp_op("Operator '>='"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]>=mem[arg2]); if (arg1==arg2) _cimg_mp_return(1); _cimg_mp_scalar2(mp_gte,arg1,arg2); } @@ -18153,13 +18246,14 @@ namespace cimg_library_suffixed { for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') _cimg_mp_op("Operator '<'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]ss; --s, --ns, --ps) if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>') _cimg_mp_op("Operator '>'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]>mem[arg2]); if (arg1==arg2) _cimg_mp_return(0); _cimg_mp_scalar2(mp_gt,arg1,arg2); } @@ -18181,8 +18276,8 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') _cimg_mp_op("Operator '<<'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); @@ -18192,8 +18287,8 @@ namespace cimg_library_suffixed { } if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar((longT)mem[arg1]<<(unsigned int)mem[arg2]); if (!arg1) _cimg_mp_return(0); if (!arg2) _cimg_mp_return(arg1); _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2); @@ -18202,8 +18297,8 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') _cimg_mp_op("Operator '>>'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); @@ -18213,8 +18308,8 @@ namespace cimg_library_suffixed { } if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar((longT)mem[arg1]>>(unsigned int)mem[arg2]); if (!arg1) _cimg_mp_return(0); if (!arg2) _cimg_mp_return(arg1); _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2); @@ -18227,15 +18322,16 @@ namespace cimg_library_suffixed { *(ps - 1)<='9')))) && level[s - expr._data]==clevel) { // Addition ('+') _cimg_mp_op("Operator '+'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (!arg2) _cimg_mp_return(arg1); if (!arg1) _cimg_mp_return(arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1] + mem[arg2]); if (code) { // Try to spot linear case 'a*b + c' CImg &pop = code.back(); if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { @@ -18259,8 +18355,8 @@ namespace cimg_library_suffixed { *(ps - 1)<='9')))) && level[s - expr._data]==clevel) { // Subtraction ('-') _cimg_mp_op("Operator '-'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (!arg2) _cimg_mp_return(arg1); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); @@ -18269,7 +18365,8 @@ namespace cimg_library_suffixed { if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2); _cimg_mp_vector2_sv(mp_sub,arg1,arg2); } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1] - mem[arg2]); if (!arg1) _cimg_mp_scalar1(mp_minus,arg2); if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b' CImg &pop = code.back(); @@ -18290,8 +18387,8 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**') _cimg_mp_op("Operator '**'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg1,1,3,2); _cimg_mp_check_type(arg2,2,3,2); if (arg2==1) _cimg_mp_return(arg1); @@ -18299,11 +18396,13 @@ namespace cimg_library_suffixed { if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { pos = vector(2); CImg::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]*mem[arg2]); if (!arg1 || !arg2) _cimg_mp_return(0); _cimg_mp_scalar2(mp_mul,arg1,arg2); } @@ -18311,35 +18410,39 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') _cimg_mp_op("Operator '//'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg1,1,3,2); _cimg_mp_check_type(arg2,2,3,2); if (arg2==1) _cimg_mp_return(arg1); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { pos = vector(2); CImg::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { pos = vector(2); CImg::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]/mem[arg2]); if (!arg1) _cimg_mp_return(0); _cimg_mp_scalar2(mp_div,arg1,arg2); } for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') _cimg_mp_op("Operator '*'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); p2 = _cimg_mp_size(arg2); if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication pos = vector(p2); CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); @@ -18348,7 +18451,8 @@ namespace cimg_library_suffixed { if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]*mem[arg2]); if (code) { // Try to spot double multiplication 'a*b*c' CImg &pop = code.back(); @@ -18367,14 +18471,15 @@ namespace cimg_library_suffixed { for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') _cimg_mp_op("Operator '/'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (arg2==1) _cimg_mp_return(arg1); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(mem[arg1]/mem[arg2]); if (!arg1) _cimg_mp_return(0); _cimg_mp_scalar2(mp_div,arg1,arg2); } @@ -18382,50 +18487,50 @@ namespace cimg_library_suffixed { for (s = se2, ns = se1; s>ss; --s, --ns) if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') _cimg_mp_op("Operator '%'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(cimg::mod(mem[arg1],mem[arg2])); _cimg_mp_scalar2(mp_modulo,arg1,arg2); } if (se1>ss) { if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) { // Unary plus ('+') _cimg_mp_op("Operator '+'"); - _cimg_mp_return(compile(ss1,se,depth1,0,is_critical)); + _cimg_mp_return(compile(ss1,se,depth1,0,block_flags)); } if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') _cimg_mp_op("Operator '-'"); - arg1 = compile(ss1,se,depth1,0,is_critical); + arg1 = compile(ss1,se,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(-mem[arg1]); _cimg_mp_scalar1(mp_minus,arg1); } if (*ss=='!') { // Logical not ('!') _cimg_mp_op("Operator '!'"); if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)' - arg1 = compile(ss2,se,depth1,0,is_critical); + arg1 = compile(ss2,se,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((bool)mem[arg1]); _cimg_mp_scalar1(mp_bool,arg1); } - arg1 = compile(ss1,se,depth1,0,is_critical); + arg1 = compile(ss1,se,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(!mem[arg1]); _cimg_mp_scalar1(mp_logical_not,arg1); } if (*ss=='~') { // Bitwise not ('~') _cimg_mp_op("Operator '~'"); - arg1 = compile(ss1,se,depth1,0,is_critical); + arg1 = compile(ss1,se,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(~(unsigned int)mem[arg1]); _cimg_mp_scalar1(mp_bitwise_not,arg1); } } @@ -18433,47 +18538,43 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') _cimg_mp_op("Operator '^^'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 2,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 2,se,depth1,0,block_flags); _cimg_mp_check_type(arg1,1,3,2); _cimg_mp_check_type(arg2,2,3,2); if (arg2==1) _cimg_mp_return(arg1); pos = vector(2); - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) CImg::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) CImg::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) CImg::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); - _cimg_mp_return(pos); - } - CImg::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code); + else + CImg::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } for (s = se2; s>ss; --s) if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') _cimg_mp_op("Operator '^'"); - arg1 = compile(ss,s,depth1,0,is_critical); - arg2 = compile(s + 1,se,depth1,0,is_critical); + arg1 = compile(ss,s,depth1,0,block_flags); + arg2 = compile(s + 1,se,depth1,0,block_flags); _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (arg2==1) _cimg_mp_return(arg1); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2); - if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) - _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) + _cimg_mp_const_scalar(std::pow(mem[arg1],mem[arg2])); switch (arg2) { case 0 : _cimg_mp_return(1); case 2 : _cimg_mp_scalar1(mp_sqr,arg1); case 3 : _cimg_mp_scalar1(mp_pow3,arg1); case 4 : _cimg_mp_scalar1(mp_pow4,arg1); default : - if (_cimg_mp_is_constant(arg2)) { + if (_cimg_mp_is_const_scalar(arg2)) { if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); } else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); } } @@ -18483,15 +18584,24 @@ namespace cimg_library_suffixed { // Percentage computation. if (*se1=='%') { - arg1 = compile(ss,se1,depth1,0,is_critical); - arg2 = _cimg_mp_is_constant(arg1)?0:constant(100); + arg1 = compile(ss,se1,depth1,0,block_flags); + arg2 = _cimg_mp_is_const_scalar(arg1)?0:const_scalar(100); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]/100); _cimg_mp_scalar2(mp_div,arg1,arg2); } + // Degree to radian postfix operator ('°' in UTF-8). + if (se2>ss && (unsigned char)*se2==0xC2 && (unsigned char)*se1==0xB0) { + arg1 = compile(ss,se2,depth1,0,block_flags); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180); + _cimg_mp_scalar1(mp_deg2rad,arg1); + } + + // Pre/post-decrement and increment. is_sth = ss1ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment + if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) { _cimg_mp_op("Operator '++'"); op = mp_self_increment; @@ -18500,11 +18610,11 @@ namespace cimg_library_suffixed { op = mp_self_decrement; } ref.assign(7); - arg1 = is_sth?compile(ss2,se,depth1,ref,is_critical): - compile(ss,se2,depth1,ref,is_critical); // Variable slot + arg1 = is_sth?compile(ss2,se,depth1,ref,block_flags): + compile(ss,se2,depth1,ref,block_flags); // Variable slot // Apply operator on a copy to prevent modifying a constant or a variable. - if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) { + if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); else arg1 = scalar1(mp_copy,arg1); } @@ -18526,14 +18636,14 @@ namespace cimg_library_suffixed { } if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); CImg::vector((ulongT)op,arg1).move_to(code); if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); + if (!imglist) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), arg1,p1,arg3).move_to(code); } else { @@ -18545,7 +18655,7 @@ namespace cimg_library_suffixed { } if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -18555,7 +18665,7 @@ namespace cimg_library_suffixed { if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); CImg::vector((ulongT)op,arg1).move_to(code); if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); + if (!imglist) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), arg1,p1,arg3,arg4,arg5,arg6).move_to(code); } else { @@ -18567,14 +18677,14 @@ namespace cimg_library_suffixed { } if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); + if (!imglist) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); } else { @@ -18586,7 +18696,7 @@ namespace cimg_library_suffixed { } if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -18595,7 +18705,7 @@ namespace cimg_library_suffixed { if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); if (p1!=~0U) { - if (!listout) _cimg_mp_return(pos); + if (!imglist) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } else { @@ -18611,7 +18721,7 @@ namespace cimg_library_suffixed { _cimg_mp_return(pos); } - if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++ + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++ CImg::vector((ulongT)op,arg1).move_to(code); _cimg_mp_return(pos); } @@ -18624,11 +18734,10 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid %slvalue '%s', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - _cimg_mp_is_constant(arg1)?"const ":"", - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + _cimg_mp_is_const_scalar(arg1)?"const ":"", + variable_name._data,s0); } // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'. @@ -18646,17 +18755,17 @@ namespace cimg_library_suffixed { !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector if (*ss2=='#') { // Index specified s0 = ss3; while (s0::vector((ulongT)(is_relative?mp_Joff:mp_Ioff), pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); } + return_new_comp = true; _cimg_mp_return(pos); } @@ -18690,64 +18800,63 @@ namespace cimg_library_suffixed { !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar if (*ss2=='#') { // Index specified s0 = ss3; while (s0ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; if (s0>ss) { // Vector element - arg1 = compile(ss,s0,depth1,0,is_critical); + arg1 = compile(ss,s0,depth1,0,block_flags); if (_cimg_mp_is_scalar(arg1)) { variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; _cimg_mp_strerr; cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); - + variable_name._data,s0); } s1 = s0 + 1; while (s1 sub-vector extraction p1 = _cimg_mp_size(arg1); - arg2 = compile(++s0,s1,depth1,0,is_critical); // Starting index + arg2 = compile(++s0,s1,depth1,0,block_flags); // Starting index s0 = ++s1; while (s0::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } // One argument -> vector value reference - arg2 = compile(++s0,se1,depth1,0,is_critical); - if (_cimg_mp_is_constant(arg2)) { // Constant index + arg2 = compile(++s0,se1,depth1,0,block_flags); + if (_cimg_mp_is_const_scalar(arg2)) { // Constant index nb = (int)mem[arg2]; if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; @@ -18756,27 +18865,26 @@ namespace cimg_library_suffixed { throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " "(vector '%s' has dimension %u), " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function, variable_name._data,nb, - variable_name._data,_cimg_mp_size(arg1), - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + variable_name._data,_cimg_mp_size(arg1),s0); } if (p_ref) { *p_ref = 1; p_ref[1] = arg1; p_ref[2] = arg2; - if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; // Prevent from being used in further optimization + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization } pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2); - memtype[pos] = -2; // Prevent from being used in further optimization + memtype[pos] = -1; // Prevent from being used in further optimization _cimg_mp_return(pos); } } // Look for a function call, an access to image value, or a parenthesis. if (*se1==')') { - if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_critical)); // Simple parentheses + if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,block_flags)); // Simple parentheses _cimg_mp_op("Value accessor '()'"); is_relative = *ss=='j' || *ss=='J'; s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } @@ -18785,9 +18893,9 @@ namespace cimg_library_suffixed { if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar if (*ss2=='#') { // Index specified s0 = ss3; while (s0::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode); for (s = ++s2; s::vector(arg3).move_to(l_opcode); ++p3; @@ -19016,7 +19120,7 @@ namespace cimg_library_suffixed { } (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; - if (_cimg_mp_is_constant(arg1)) { + if (_cimg_mp_is_const_scalar(arg1)) { p3-=1; // Number of args if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1); else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]); @@ -19024,47 +19128,49 @@ namespace cimg_library_suffixed { if (p2) { pos = vector(p2); std::memset(&mem[pos] + 1,0,p2*sizeof(double)); + return_new_comp = true; _cimg_mp_return(pos); } else _cimg_mp_return(0); } pos = opcode[1] = p2?vector(p2):scalar(); opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"asin(",5)) { // Arcsin _cimg_mp_op("Function 'asin()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::asin(mem[arg1])); _cimg_mp_scalar1(mp_asin,arg1); } if (!std::strncmp(ss,"atan(",5)) { // Arctan _cimg_mp_op("Function 'atan()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::atan(mem[arg1])); _cimg_mp_scalar1(mp_atan,arg1); } if (!std::strncmp(ss,"atan2(",6)) { // Arctan2 _cimg_mp_op("Function 'atan2()'"); s1 = ss6; while (s1::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code); _cimg_mp_return_nan(); @@ -19081,33 +19187,41 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"bool(",5)) { // Boolean cast _cimg_mp_op("Function 'bool()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((bool)mem[arg1]); _cimg_mp_scalar1(mp_bool,arg1); } if (!std::strncmp(ss,"begin(",6)) { // Begin _cimg_mp_op("Function 'begin()'"); - code.swap(code_begin); - arg1 = compile(ss6,se1,depth1,p_ref,true); - code.swap(code_begin); - _cimg_mp_return(arg1); + s1 = ss6; while (s1::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"ceil(",5)) { // Ceil _cimg_mp_op("Function 'ceil()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::ceil(mem[arg1])); _cimg_mp_scalar1(mp_ceil,arg1); } if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential _cimg_mp_op("Function 'cexp()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm _cimg_mp_op("Function 'clog()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine _cimg_mp_op("Function 'ccos()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"csin(",5)) { // Complex sine _cimg_mp_op("Function 'csin()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sin,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent _cimg_mp_op("Function 'ctan()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine _cimg_mp_op("Function 'ccosh()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine _cimg_mp_op("Function 'csinh()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sinh,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent _cimg_mp_op("Function 'ctanh()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,0,3,2); pos = vector(2); if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code); else CImg::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -19238,20 +19361,20 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'copy()'"); ref.assign(14); s1 = ss5; while (s1=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]); } if (_cimg_mp_is_vector(arg2)) { - if (arg3==~0U) arg3 = constant(_cimg_mp_size(arg2)); + if (arg3==~0U) arg3 = const_scalar(_cimg_mp_size(arg2)); if (!ref[7]) ++arg2; if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]); } @@ -19274,189 +19397,289 @@ namespace cimg_library_suffixed { CImg(1,22).move_to(code); code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6); code.back().get_shared_rows(8,21).fill(ref); - _cimg_mp_return(p1); + _cimg_mp_return_nan(); } if (!std::strncmp(ss,"cos(",4)) { // Cosine _cimg_mp_op("Function 'cos()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::cos(mem[arg1])); _cimg_mp_scalar1(mp_cos,arg1); } if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine _cimg_mp_op("Function 'cosh()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::cosh(mem[arg1])); _cimg_mp_scalar1(mp_cosh,arg1); } + if (!std::strncmp(ss,"cov(",4)) { // Covariance + _cimg_mp_op("Function 'cov()'"); + s1 = ss4; while (s1::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1); _cimg_mp_return(arg1); } - if (!std::strncmp(ss,"crop(",5)) { // Image crop + if (!std::strncmp(ss,"crop(",5)) { // Image or vector crop _cimg_mp_op("Function 'crop()'"); + is_sth = false; // is image crop ? if (*ss5=='#') { // Index specified s0 = ss6; while (s0::sequence(_cimg_mp_size(arg1),arg1 + 1, - arg1 + (ulongT)_cimg_mp_size(arg1)); - opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode); - is_sth = true; - } else { - _cimg_mp_check_type(arg1,pos + 1,1,0); - CImg::vector(arg1).move_to(l_opcode); - } + arg1 = compile(s,ns,depth1,0,block_flags); + if (pos==1 && _cimg_mp_is_scalar(arg1)) is_sth = true; + else if (pos>1) _cimg_mp_check_type(arg1,pos,1,0); + CImg::vector(arg1).move_to(l_opcode); s = ns; } (l_opcode>'y').move_to(opcode); - arg1 = 0; arg2 = (p1!=~0U); - switch (opcode._height) { - case 0 : case 1 : - CImg::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); - break; - case 2 : - CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); - arg1 = arg2 + 2; - break; - case 3 : - CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); - arg1 = arg2 + 2; - break; - case 4 : - CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary). - move_to(opcode); - arg1 = arg2 + (is_sth?2:3); - break; - case 5 : - CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). - move_to(opcode); - arg1 = arg2 + (is_sth?2:3); - break; - case 6 : - CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, - _cimg_mp_boundary).move_to(opcode); - arg1 = arg2 + (is_sth?2:4); - break; - case 7 : - CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, - opcode[6]).move_to(opcode); - arg1 = arg2 + (is_sth?2:4); - break; - case 8 : - CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], - opcode[7],_cimg_mp_boundary).move_to(opcode); - arg1 = arg2 + (is_sth?2:5); - break; - case 9 : - arg1 = arg2 + (is_sth?2:5); - break; - default : // Error -> too much arguments - _cimg_mp_strerr; - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Too much arguments specified, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - - _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0); - _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0); - _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0); - _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0); - if (opcode[4]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3); - opcode[4] = (ulongT)mem[opcode[4]]; - } - if (opcode[5]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3); - opcode[5] = (ulongT)mem[opcode[5]]; - } - if (opcode[6]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3); - opcode[6] = (ulongT)mem[opcode[6]]; - } - if (opcode[7]!=(ulongT)~0U) { - _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3); - opcode[7] = (ulongT)mem[opcode[7]]; - } - _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0); - - if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U || - opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) { - p2 = 0; - if (p1!=~0U) { - _cimg_mp_check_constant(p1,1,1); - p2 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); - } - const CImg &img = p1!=~0U?listin[p2]:imgin; - if (!img) { + if (!opcode || is_sth) { // Image crop + arg1 = 0; arg2 = (p1!=~0U); + switch (opcode._height) { + case 0 : case 1 : + CImg::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); + break; + case 2 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); + arg1 = 2 + arg2; break; + case 3 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); + arg1 = 2 + arg2; break; + case 4 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary). + move_to(opcode); + arg1 = 3 + arg2; break; + case 5 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). + move_to(opcode); + arg1 = 3 + arg2; break; + case 6 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + _cimg_mp_boundary).move_to(opcode); + arg1 = 4 + arg2; break; + case 7 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + opcode[6]).move_to(opcode); + arg1 = 4 + arg2; break; + case 8 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], + opcode[7],_cimg_mp_boundary).move_to(opcode); + arg1 = 5 + arg2; break; + case 9 : + arg1 = 5 + arg2; break; + default : // Error -> too much arguments _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Cannot crop empty image when " - "some xyzc-coordinates are unspecified, in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + "CImg<%s>::%s: %s: Too much arguments specified, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s0); } - if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width; - if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height; - if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth; - if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum; - } - pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7])); - CImg::vector((ulongT)mp_crop, - pos,p1, - *opcode,opcode[1],opcode[2],opcode[3], - opcode[4],opcode[5],opcode[6],opcode[7], - opcode[8]).move_to(code); + if (opcode[4]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[4],arg1,3); + opcode[4] = (ulongT)mem[opcode[4]]; + } + if (opcode[5]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[5],arg1 + 1,3); + opcode[5] = (ulongT)mem[opcode[5]]; + } + if (opcode[6]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[6],arg1 + 2,3); + opcode[6] = (ulongT)mem[opcode[6]]; + } + if (opcode[7]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[7],arg1 + 3,3); + opcode[7] = (ulongT)mem[opcode[7]]; + } + _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0); + + if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U || + opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) { + p2 = 0; + if (p1!=~0U) { + _cimg_mp_check_const_scalar(p1,1,1); + p2 = (unsigned int)cimg::mod((int)mem[p1],imglist.width()); + } + const CImg &img = p1!=~0U?imglist[p2]:imgin; + if (!img) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Cannot crop empty image when " + "some xyzc-coordinates are unspecified, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s0); + } + if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width; + if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height; + if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth; + if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum; + } + + pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7])); + CImg::vector((ulongT)mp_image_crop, + pos,p1, // 1-2: res,#ind + *opcode,opcode[1],opcode[2],opcode[3], // 3-6: x,y,z,c + opcode[4],opcode[5],opcode[6],opcode[7], // 7-10: dx,dy,dz,dc + opcode[8]).move_to(code); // 11: boundary conditions + + } else { // Vector crop + switch (opcode._height) { + case 5 : case 6 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + 0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); + break; + case 7 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + opcode[5],0,0,0,opcode[6],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); + arg1 = 7; break; + case 8 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + opcode[5],0,0,0,opcode[6],~0U,~0U,~0U,opcode[7]).move_to(opcode); + arg1 = 7; break; + case 9 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + opcode[5],opcode[6],0,0,opcode[7],opcode[8],~0U,~0U,_cimg_mp_boundary). + move_to(opcode); + arg1 = 8; break; + case 10 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + opcode[5],opcode[6],0,0,opcode[7],opcode[8],~0U,~0U,opcode[9]). + move_to(opcode); + arg1 = 8; break; + case 11 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + opcode[5],opcode[6],opcode[7],0,opcode[8],opcode[9],opcode[10],~0U, + _cimg_mp_boundary).move_to(opcode); + arg1 = 9; break; + case 12 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + opcode[5],opcode[6],opcode[7],0,opcode[8],opcode[9],opcode[10],~0U, + opcode[11]).move_to(opcode); + arg1 = 9; break; + case 13 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4], + opcode[5],opcode[6],opcode[7],opcode[8],opcode[9],opcode[10],opcode[11], + opcode[12],_cimg_mp_boundary).move_to(opcode); + arg1 = 10; break; + case 14 : + arg1 = 10; break; + default : // Error -> too few or too much arguments + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too %s arguments specified, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + opcode._height<5?"few":"much",s0); + } + + _cimg_mp_check_const_scalar((unsigned int)opcode[1],2,3); // w + opcode[1] = (ulongT)mem[opcode[1]]; + _cimg_mp_check_const_scalar((unsigned int)opcode[2],3,3); // h + opcode[2] = (ulongT)mem[opcode[2]]; + _cimg_mp_check_const_scalar((unsigned int)opcode[3],4,3); // d + opcode[3] = (ulongT)mem[opcode[3]]; + _cimg_mp_check_const_scalar((unsigned int)opcode[4],5,3); // s + opcode[4] = (ulongT)mem[opcode[4]]; + p1 = _cimg_mp_size((unsigned int)opcode[0]); + arg2 = (unsigned int)opcode[1]; + arg3 = (unsigned int)opcode[2]; + arg4 = (unsigned int)opcode[3]; + arg5 = (unsigned int)opcode[4]; + if (arg2*arg3*arg4*arg5!=p1) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Input vector size (%lu values) and its specified " + "geometry (%u,%u,%u,%u) (%lu values) do not match.", + pixel_type(),_cimg_mp_calling_function,s_op, + p1,arg2,arg3,arg4,arg5,(ulongT)arg2*arg3*arg4*arg5); + + if (opcode[9]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[9],arg1,3); + opcode[9] = (ulongT)mem[opcode[9]]; + } else opcode[9] = opcode[1]; + if (opcode[10]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[10],arg1 + 1,3); + opcode[10] = (ulongT)mem[opcode[10]]; + } else opcode[10] = opcode[2]; + if (opcode[11]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[11],arg1 + 2,3); + opcode[11] = (ulongT)mem[opcode[11]]; + } else opcode[11] = opcode[3]; + if (opcode[12]!=(ulongT)~0U) { + _cimg_mp_check_const_scalar((unsigned int)opcode[12],arg1 + 3,3); + opcode[12] = (ulongT)mem[opcode[12]]; + } else opcode[12] = opcode[4]; + _cimg_mp_check_type((unsigned int)opcode[13],arg1 + 4,1,0); + + pos = vector((unsigned int)(opcode[9]*opcode[10]*opcode[11]*opcode[12])); + CImg::vector((ulongT)mp_vector_crop_ext, + pos,*opcode, // 1-2: res,S + opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s + opcode[5],opcode[6],opcode[7],opcode[8], // 7-10: x,y,z,c + opcode[9],opcode[10],opcode[11],opcode[12], // 11-14: dx,dy,dz,dc + opcode[13]).move_to(code); // 15: boundary conditions + } + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"cross(",6)) { // Cross product _cimg_mp_op("Function 'cross()'"); s1 = ss6; while (s1::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"cut(",4)) { // Cut _cimg_mp_op("Function 'cut()'"); s1 = ss4; while (s1val2?val2:val); + _cimg_mp_const_scalar(valval2?val2:val); } _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); } @@ -19469,11 +19692,12 @@ namespace cimg_library_suffixed { 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode - 11,11,11, // [15]=xcenter, [16]=ycenter, [17]=zcenter (default value:-1) + ~0U,~0U,~0U, // [15]=xcenter, [16]=ycenter, [17]=zcenter 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart - 11,11,11, // [21]=xend, [22]=yend, [23]=zend (default value: -1) + ~0U,~0U,~0U, // [21]=xend, [22]=yend, [23]=zend 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride - 1,1,1 }; // [27]=xdilation, [28]=ydilation, [29]=zdilation + 1,1,1, // [27]=xdilation, [28]=ydilation, [29]=zdilation, + 0 }; // [30]=interpolation_type l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'! CImg(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode); @@ -19482,47 +19706,47 @@ namespace cimg_library_suffixed { for (s = std::strchr(ss,'(') + 1; s=opcode._height) { + if (arg1<12 || arg1>opcode._height) { _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: %s arguments provided, in expression '%s%s%s'.", + "CImg<%s>::%s: %s: %s arguments provided, in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - arg1<12?"Not enough":"Too much", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + arg1<12?"Not enough":"Too much",s0); } _cimg_mp_check_type(opcode[2],1,2,0); // A - _cimg_mp_check_constant(opcode[3],2,3); // wA - _cimg_mp_check_constant(opcode[4],3,3); // hA - _cimg_mp_check_constant(opcode[5],4,3); // dA - _cimg_mp_check_constant(opcode[6],5,3); // sA + _cimg_mp_check_const_scalar(opcode[3],2,3); // wA + _cimg_mp_check_const_scalar(opcode[4],3,3); // hA + _cimg_mp_check_const_scalar(opcode[5],4,3); // dA + _cimg_mp_check_const_scalar(opcode[6],5,3); // sA _cimg_mp_check_type(opcode[7],6,2,0); // M - _cimg_mp_check_constant(opcode[8],7,3); // wM - _cimg_mp_check_constant(opcode[9],8,3); // hM - _cimg_mp_check_constant(opcode[10],9,3); // dM - _cimg_mp_check_constant(opcode[11],10,3); // sM + _cimg_mp_check_const_scalar(opcode[8],7,3); // wM + _cimg_mp_check_const_scalar(opcode[9],8,3); // hM + _cimg_mp_check_const_scalar(opcode[10],9,3); // dM + _cimg_mp_check_const_scalar(opcode[11],10,3); // sM _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized - _cimg_mp_check_constant(opcode[14],13,1); // channel_mode - _cimg_mp_check_type(opcode[15],14,1,0); // xcenter - _cimg_mp_check_type(opcode[16],15,1,0); // ycenter - _cimg_mp_check_type(opcode[17],16,1,0); // zcenter - _cimg_mp_check_constant(opcode[18],17,1); // xstart - _cimg_mp_check_constant(opcode[19],18,1); // ystart - _cimg_mp_check_constant(opcode[20],19,1); // zstart - _cimg_mp_check_constant(opcode[21],20,1); // xend - _cimg_mp_check_constant(opcode[22],21,1); // yend - _cimg_mp_check_constant(opcode[23],22,1); // zend - _cimg_mp_check_constant(opcode[24],23,3); // xstride - _cimg_mp_check_constant(opcode[25],24,3); // ystride - _cimg_mp_check_constant(opcode[26],25,3); // zstride + _cimg_mp_check_const_scalar(opcode[14],13,1); // channel_mode + if (opcode[15]!=~0U) _cimg_mp_check_type(opcode[15],14,1,0); // xcenter + if (opcode[16]!=~0U) _cimg_mp_check_type(opcode[16],15,1,0); // ycenter + if (opcode[17]!=~0U) _cimg_mp_check_type(opcode[17],16,1,0); // zcenter + _cimg_mp_check_const_scalar(opcode[18],17,1); // xstart + _cimg_mp_check_const_scalar(opcode[19],18,1); // ystart + _cimg_mp_check_const_scalar(opcode[20],19,1); // zstart + if (opcode[21]!=~0U) _cimg_mp_check_const_scalar(opcode[21],20,1); // xend + if (opcode[22]!=~0U) _cimg_mp_check_const_scalar(opcode[22],21,1); // yend + if (opcode[23]!=~0U) _cimg_mp_check_const_scalar(opcode[23],22,1); // zend + _cimg_mp_check_const_scalar(opcode[24],23,0); // xstride + _cimg_mp_check_const_scalar(opcode[25],24,0); // ystride + _cimg_mp_check_const_scalar(opcode[26],25,0); // zstride _cimg_mp_check_type(opcode[27],26,1,0); // xdilation _cimg_mp_check_type(opcode[28],27,1,0); // ydilation _cimg_mp_check_type(opcode[29],28,1,0); // zdilation + _cimg_mp_check_type(opcode[30],29,1,0); // interpolation_type const unsigned int wA = (unsigned int)mem[opcode[3]], @@ -19533,22 +19757,22 @@ namespace cimg_library_suffixed { hM = (unsigned int)mem[opcode[9]], dM = (unsigned int)mem[opcode[10]], sM = (unsigned int)mem[opcode[11]], - channel_mode = (unsigned int)mem[opcode[14]], - xstart = std::min((unsigned int)mem[opcode[18]],wA - 1), - ystart = std::min((unsigned int)mem[opcode[19]],hA - 1), - zstart = std::min((unsigned int)mem[opcode[20]],dA - 1), - xend = std::min((unsigned int)mem[opcode[21]],wA - 1), - yend = std::min((unsigned int)mem[opcode[22]],hA - 1), - zend = std::min((unsigned int)mem[opcode[23]],dA - 1); + channel_mode = (unsigned int)mem[opcode[14]]; + const int + xstart = (int)mem[opcode[18]], + ystart = (int)mem[opcode[19]], + zstart = (int)mem[opcode[20]], + xend = opcode[21]!=~0U?(int)mem[opcode[21]]:wA - 1, + yend = opcode[22]!=~0U?(int)mem[opcode[22]]:hA - 1, + zend = opcode[23]!=~0U?(int)mem[opcode[23]]:dA - 1; if (xstart>xend || ystart>yend || zstart>zend) { _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid xyz-start/end arguments " - "(start = (%u,%u,%u), end = (%u,%u,%u)), in expression '%s%s%s'.", + "(start = (%d,%d,%d), end = (%d,%d,%d)), in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - xstart,ystart,zstart,xend,yend,zend, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + xstart,ystart,zstart,xend,yend,zend,s0); } const float @@ -19560,16 +19784,16 @@ namespace cimg_library_suffixed { _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - xstride,ystride,zstride, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + xstride,ystride,zstride,s0); } - arg2 = 1 + (unsigned int)std::floor((xend - xstart)/xstride); - arg3 = 1 + (unsigned int)std::floor((yend - ystart)/ystride); - arg4 = 1 + (unsigned int)std::floor((zend + zstart)/zstride); - arg5 = channel_mode==0?sM:channel_mode==1?std::max(sA,sM):sA*sM; + arg2 = xend - xstart + 1; + arg3 = yend - ystart + 1; + arg4 = zend - zstart + 1; + arg5 = !channel_mode?sA*sM:channel_mode==1?std::max(sA,sM): + channel_mode==2?std::max(sA,sM)/std::min(sA,sM):1U; opcode[1] = pos = vector(arg2*arg3*arg4*arg5); opcode[3] = (ulongT)wA; @@ -19588,6 +19812,7 @@ namespace cimg_library_suffixed { opcode[22] = (ulongT)yend; opcode[23] = (ulongT)zend; opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } break; @@ -19596,31 +19821,135 @@ namespace cimg_library_suffixed { if (*ss1=='(') { // Image depth _cimg_mp_op("Function 'd()'"); if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss3,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_d,pos,p1).move_to(code); + _cimg_mp_scalar1(mp_image_d,p1); + } + + if (!std::strncmp(ss,"da_back(",8) || + !std::strncmp(ss,"da_pop(",7)) { // Get latest element in a dynamic array + if (!is_inside_critical) is_parallelizable = false; + const bool is_pop = *ss3=='p'; + _cimg_mp_op(is_pop?"Function 'da_pop()'":"Function 'da_back()'"); + s0 = ss + (is_pop?7:8); + if (*s0=='#') { // Index specified + s1 = ++s0; while (s11) pos = vector(p2); else pos = scalar(); // Return vector or scalar result + CImg::vector((ulongT)mp_da_back_or_pop,pos,p2,p1,is_pop).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } + if (!std::strncmp(ss,"da_insert(",10) || + !std::strncmp(ss,"da_push(",8)) { // Insert element(s) in a dynamic array + if (!is_inside_critical) is_parallelizable = false; + const bool is_push = *ss3=='p'; + _cimg_mp_op(is_push?"Function 'da_push()'":"Function 'da_insert()'"); + s0 = ss + (is_push?8:10); + if (*s0=='#') { // Index specified + s1 = ++s0; while (s1::vector((ulongT)mp_da_insert_or_push,_cimg_mp_slot_nan,p1,arg1,0,0).move_to(l_opcode); + p3 = p1==~0U?2:3; + p1 = ~0U; + for (s = s1; s::vector(arg2).move_to(l_opcode); + s = ns; + ++p3; + } + if (p1==~0U) compile(++s1,se1,depth1,0,block_flags); // Missing element -> error + (l_opcode>'y').move_to(opcode); + opcode[4] = p1; + opcode[5] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"da_freeze(",10)) { // Freeze dynamic array + if (!is_inside_critical) is_parallelizable = false; + _cimg_mp_op("Function 'da_freeze()'"); + s0 = ss + 10; + if (*s0=='#') { // Index specified + s1 = ++s0; while (s1::vector((ulongT)mp_da_freeze,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"da_remove(",10)) { // Remove element(s) in a dynamic array + if (!is_inside_critical) is_parallelizable = false; + _cimg_mp_op("Function 'da_remove()'"); + if (ss[10]=='#') { // Index specified + s0 = ss + 11; while (s0::vector((ulongT)mp_da_remove,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"da_size(",8)) { // Size of a dynamic array + if (!is_inside_critical) is_parallelizable = false; + _cimg_mp_op("Function 'da_size()'"); + if (ss[8]=='#') { // Index specified + s0 = ss + 9; while (s0::vector((ulongT)mp_date,pos,_cimg_mp_size(pos), arg1,arg1==~0U?~0U:_cimg_mp_size(arg1), arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"debug(",6)) { // Print debug info _cimg_mp_op("Function 'debug()'"); p1 = code._width; - arg1 = compile(ss6,se1,depth1,p_ref,is_critical); + arg1 = compile(ss6,se1,depth1,p_ref,block_flags); *se1 = 0; variable_name.assign(CImg::string(ss6,true,true).unroll('y'),true); cimg::strpare(variable_name,false,true); @@ -19632,6 +19961,14 @@ namespace cimg_library_suffixed { _cimg_mp_return(arg1); } + if (!std::strncmp(ss,"deg2rad(",8)) { // Degrees to radians + _cimg_mp_op("Function 'deg2rad()'"); + arg1 = compile(ss8,se1,depth1,0,block_flags); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180); + _cimg_mp_scalar1(mp_deg2rad,arg1); + } + if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image _cimg_mp_op("Function 'display()'"); if (pexpr[se2 - expr._data]=='(') { // no arguments? @@ -19640,18 +19977,18 @@ namespace cimg_library_suffixed { } if (*ss8!='#') { // Vector s1 = ss8; while (s1::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code); _cimg_mp_return_nan(); } @@ -19690,7 +20027,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"det(",4)) { // Matrix determinant _cimg_mp_op("Function 'det()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); _cimg_mp_check_matrix_square(arg1,1); p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); _cimg_mp_scalar2(mp_det,arg1,p1); @@ -19702,7 +20039,7 @@ namespace cimg_library_suffixed { for (s = ss5; s::sequence(_cimg_mp_size(arg2),arg2 + 1, arg2 + (ulongT)_cimg_mp_size(arg2)). @@ -19716,14 +20053,15 @@ namespace cimg_library_suffixed { opcode[1] = pos; opcode[2] = opcode._height; opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"dot(",4)) { // Dot product _cimg_mp_op("Function 'dot()'"); s1 = ss4; while (s1::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1), - p1>=arg6 && !_cimg_mp_is_constant(p1), - p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + p1>=arg6 && !_cimg_mp_is_const_scalar(p1), + p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1); _cimg_mp_return(p1); } if (!std::strncmp(ss,"draw(",5)) { // Draw image - if (!is_critical) is_parallelizable = false; _cimg_mp_op("Function 'draw()'"); if (*ss5=='#') { // Index specified s0 = ss6; while (s01) { - arg3 = arg2 + 1; - if (p2>2) { - arg4 = arg3 + 1; - if (p2>3) arg5 = arg4 + 1; - } - } - ++s0; - is_sth = true; + + for (s = s0; s::vector(arg1).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + + is_sth = p1==~0U && opcode._height>5 && _cimg_mp_is_vector((unsigned int)opcode[5]); // Is vector drawing? + if ((is_sth && (opcode._height<6 || opcode._height>17)) || + (!is_sth && (opcode._height<1 || opcode._height>12))) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too %s arguments specified, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + opcode._height>12?"much":"few",s0); + } + + if (is_sth) { // Drawing in a vector + _cimg_mp_check_type((unsigned int)*opcode,1,2,0); // D + _cimg_mp_check_type((unsigned int)opcode[1],2,1,0); // w + _cimg_mp_check_type((unsigned int)opcode[2],3,1,0); // h + _cimg_mp_check_type((unsigned int)opcode[3],4,1,0); // d + _cimg_mp_check_type((unsigned int)opcode[4],5,1,0); // s + + if (opcode._height<8 || (opcode._height<10 && _cimg_mp_is_vector((unsigned int)opcode[7]))) { + // D,w,h,d,s,S[,opac,M,maxM] + if (opcode._height>6) _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // opac + if (opcode._height>8) _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // maxM + CImg::vector((ulongT)mp_vector_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD + opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s + opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS + 0,0,0,0, // 9-12: x,y,z,c + ~0U,~0U,~0U,~0U, // 13-16: dx,dy,dz,dc + opcode._height<7?1:opcode[6], // 17: opac + opcode._height<8?~0U:opcode[7], // 18: M + opcode._height<8?0:_cimg_mp_size((unsigned int)opcode[7]), // 19: sizM + opcode._height<9?1:opcode[8]).move_to(code); // 20: maxM + } else if (opcode._height<10 || (opcode._height<12 && _cimg_mp_is_vector((unsigned int)opcode[9]))) { + // D,w,h,d,s,S,x,dx[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // dx + if (opcode._height>8) _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // opac + if (opcode._height>10) _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // maxM + CImg::vector((ulongT)mp_vector_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD + opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s + opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS + opcode[6],0,0,0, // 9-12: x,y,z,c + opcode[7],~0U,~0U,~0U, // 13-16: dx,dy,dz,dc + opcode._height<9?1:opcode[8], // 17: opac + opcode._height<10?~0U:opcode[9], // 18: M + opcode._height<10?0:_cimg_mp_size((unsigned int)opcode[9]), // 19: sizM + opcode._height<11?1:opcode[10]).move_to(code); // 20: maxM + } else if (opcode._height<12 || (opcode._height<14 && _cimg_mp_is_vector((unsigned int)opcode[11]))) { + // D,w,h,d,s,S,x,y,dx,dy[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y + _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // dx + _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // dy + if (opcode._height>10) _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // opac + if (opcode._height>12) _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // maxM + CImg::vector((ulongT)mp_vector_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD + opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s + opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS + opcode[6],opcode[7],0,0, // 9-12: x,y,z,c + opcode[8],opcode[9],~0U,~0U, // 13-16: dx,dy,dz,dc + opcode._height<11?1:opcode[10], // 17: opac + opcode._height<12?~0U:opcode[11], // 18: M + opcode._height<12?0:_cimg_mp_size((unsigned int)opcode[11]), // 19: sizM + opcode._height<13?1:opcode[12]).move_to(code); // 20: maxM + } else if (opcode._height<14 || (opcode._height<16 && _cimg_mp_is_vector((unsigned int)opcode[13]))) { + // D,w,h,d,s,S,x,y,z,dx,dy,dz[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y + _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // z + _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // dx + _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // dy + _cimg_mp_check_type((unsigned int)opcode[11],12,1,0); // dz + if (opcode._height>12) _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // opac + if (opcode._height>14) _cimg_mp_check_type((unsigned int)opcode[14],15,1,0); // maxM + CImg::vector((ulongT)mp_vector_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD + opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s + opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS + opcode[6],opcode[7],opcode[8],0, // 9-12: x,y,z,c + opcode[9],opcode[10],opcode[11],~0U, // 13-16: dx,dy,dz,dc + opcode._height<13?1:opcode[12], // 17: opac + opcode._height<14?~0U:opcode[13], // 18: M + opcode._height<14?0:_cimg_mp_size((unsigned int)opcode[13]), // 19: sizM + opcode._height<15?1:opcode[14]).move_to(code); // 20: maxM + } else if (opcode._height<16 || (opcode._height<18 && _cimg_mp_is_vector((unsigned int)opcode[15]))) { + // D,w,h,d,s,S,x,y,z,c,dx,dy,dz,dc[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y + _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // z + _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // c + _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // dx + _cimg_mp_check_type((unsigned int)opcode[11],12,1,0); // dy + _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // dz + _cimg_mp_check_type((unsigned int)opcode[13],14,1,0); // dc + if (opcode._height>14) _cimg_mp_check_type((unsigned int)opcode[14],15,1,0); // opac + if (opcode._height>16) _cimg_mp_check_type((unsigned int)opcode[16],17,1,0); // maxM + CImg::vector((ulongT)mp_vector_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD + opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s + opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS + opcode[6],opcode[7],opcode[8],opcode[9], // 9-12: x,y,z,c + opcode[10],opcode[11],opcode[12],opcode[13], // 13-16: dx,dy,dz,dc + opcode._height<15?1:opcode[14], // 17: opac + opcode._height<16?~0U:opcode[15], // 18: M + opcode._height<16?0:_cimg_mp_size((unsigned int)opcode[15]), // 19: sizM + opcode._height<17?1:opcode[16]).move_to(code); // 20: maxM } else { - if (s0::%s: %s: Invalid types in specified arguments, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s0); + } + + } else { // Drawing in an image + if (!is_inside_critical) is_parallelizable = false; + arg1 = p1!=~0U; + _cimg_mp_check_type((unsigned int)*opcode,1 + arg1,2,0); // S + if (opcode._height<3 || (opcode._height<5 && _cimg_mp_is_vector((unsigned int)opcode[2]))) { + // S[,opac,M,maxM] + if (opcode._height>1) _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // opac + if (opcode._height>3) _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // maxM + CImg::vector((ulongT)mp_image_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind + 0,0,0,0, // 4-7: x,y,z,c + ~0U,~0U,~0U,~0U, // 8-11: dx,dy,dz,dc + opcode._height<2?1:opcode[1], // 12: opac + opcode._height<3?~0U:opcode[2], // 13: M + opcode._height<3?0:_cimg_mp_size((unsigned int)opcode[2]), // 14: sizM + opcode._height<4?1:opcode[3]).move_to(code); // 15: maxM + } else if (opcode._height<5 || (opcode._height<7 && _cimg_mp_is_vector((unsigned int)opcode[4]))) { + // x,dx,S[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // dx + if (opcode._height>3) _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // opac + if (opcode._height>5) _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // maxM + CImg::vector((ulongT)mp_image_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind + opcode[1],0,0,0, // 4-7: x,y,z,c + opcode[2],~0U,~0U,~0U, // 8-11: dx,dy,dz,dc + opcode._height<4?1:opcode[3], // 12: opac + opcode._height<5?~0U:opcode[4], // 13: M + opcode._height<5?0:_cimg_mp_size((unsigned int)opcode[4]), // 14: sizM + opcode._height<6?1:opcode[5]).move_to(code); // 15: maxM + } else if (opcode._height<7 || (opcode._height<9 && _cimg_mp_is_vector((unsigned int)opcode[6]))) { + // x,y,dx,dy,S[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y + _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // dx + _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // dy + if (opcode._height>5) _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // opac + if (opcode._height>7) _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // maxM + CImg::vector((ulongT)mp_image_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind + opcode[1],opcode[2],0,0, // 4-7: x,y,z,c + opcode[3],opcode[4],~0U,~0U, // 8-11: dx,dy,dz,dc + opcode._height<6?1:opcode[5], // 12: opac + opcode._height<7?~0U:opcode[6], // 13: M + opcode._height<7?0:_cimg_mp_size((unsigned int)opcode[6]), // 14: sizM + opcode._height<8?1:opcode[7]).move_to(code); // 15: maxM + } else if (opcode._height<9 || (opcode._height<11 && _cimg_mp_is_vector((unsigned int)opcode[8]))) { + // x,y,z,dx,dy,dz,S[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y + _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // z + _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // dx + _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // dy + _cimg_mp_check_type((unsigned int)opcode[6],7 + arg1,1,0); // dz + if (opcode._height>7) _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // opac + if (opcode._height>9) _cimg_mp_check_type((unsigned int)opcode[9],10 + arg1,1,0); // maxM + CImg::vector((ulongT)mp_image_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind + opcode[1],opcode[2],opcode[3],0, // 4-7: x,y,z,c + opcode[4],opcode[5],opcode[6],~0U, // 8-11: dx,dy,dz,dc + opcode._height<8?1:opcode[7], // 12: opac + opcode._height<9?~0U:opcode[8], // 13: M + opcode._height<9?0:_cimg_mp_size((unsigned int)opcode[8]), // 14: sizM + opcode._height<10?1:opcode[9]).move_to(code); // 15: maxM + } else if (opcode._height<11 || (opcode._height<13 && _cimg_mp_is_vector((unsigned int)opcode[10]))) { + // x,y,z,c,dx,dy,dz,dc,S[,opac,M,maxM] + _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x + _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y + _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // z + _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // c + _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // dx + _cimg_mp_check_type((unsigned int)opcode[6],7 + arg1,1,0); // dy + _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // dz + _cimg_mp_check_type((unsigned int)opcode[8],9 + arg1,1,0); // dc + if (opcode._height>9) _cimg_mp_check_type((unsigned int)opcode[9],10 + arg1,1,0); // opac + if (opcode._height>11) _cimg_mp_check_type((unsigned int)opcode[11],12 + arg1,1,0); // maxM + CImg::vector((ulongT)mp_image_draw, + *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind + opcode[1],opcode[2],opcode[3],opcode[4], // 4-7: x,y,z,c + opcode[5],opcode[6],opcode[7],opcode[8], // 8-11: dx,dy,dz,dc + opcode._height<10?1:opcode[9], // 12: opac + opcode._height<11?~0U:opcode[10], // 13: M + opcode._height<11?0:_cimg_mp_size((unsigned int)opcode[10]), // 14: sizM + opcode._height<12?1:opcode[11]).move_to(code); // 15: maxM + } else { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid types in specified arguments, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s0); } } - - l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'! - CImg::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5, - 0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode); - - arg2 = arg3 = arg4 = arg5 = ~0U; - p2 = p1!=~0U?0:1; - if (s0::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode); - for (s = ss5; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); s = ns; } @@ -19874,33 +20334,36 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector _cimg_mp_op("Function 'eig()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); _cimg_mp_check_matrix_square(arg1,1); p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); pos = vector((p1 + 1)*p1); CImg::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; _cimg_mp_op("Function 'ellipse()'"); if (*ss8=='#') { // Index specified s0 = ss + 9; while (s0::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); - for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, arg2 + (ulongT)_cimg_mp_size(arg2)). move_to(l_opcode); - else CImg::vector(arg2).move_to(l_opcode); + else + CImg::vector(arg2).move_to(l_opcode); s = ns; } (l_opcode>'y').move_to(opcode); @@ -19909,39 +20372,55 @@ namespace cimg_library_suffixed { _cimg_mp_return_nan(); } + if (!std::strncmp(ss,"erf(",4)) { // Error function + _cimg_mp_op("Function 'erf()'"); + arg1 = compile(ss4,se1,depth1,0,block_flags); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::erf(mem[arg1])); + _cimg_mp_scalar1(mp_erf,arg1); + } + + if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function + _cimg_mp_op("Function 'erfinv()'"); + arg1 = compile(ss7,se1,depth1,0,block_flags); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::erfinv(mem[arg1])); + _cimg_mp_scalar1(mp_erfinv,arg1); + } + if (!std::strncmp(ss,"exp(",4)) { // Exponential _cimg_mp_op("Function 'exp()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::exp(mem[arg1])); _cimg_mp_scalar1(mp_exp,arg1); } if (!std::strncmp(ss,"expr(",5)) { // Vector from expression _cimg_mp_op("Function 'expr()'"); s1 = ss5; while (s1::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"eye(",4)) { // Identity matrix _cimg_mp_op("Function 'eye()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); - _cimg_mp_check_constant(arg1,1,3); + arg1 = compile(ss4,se1,depth1,0,block_flags); + _cimg_mp_check_const_scalar(arg1,1,3); p1 = (unsigned int)mem[arg1]; pos = vector(p1*p1); CImg::vector((ulongT)mp_eye,pos,p1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"end(",4)) { // End _cimg_mp_op("Function 'end()'"); - code.swap(code_end); - compile(ss4,se1,depth1,p_ref,true); - code.swap(code_end); - is_end_code = true; + s1 = ss4; while (s1uint conversion _cimg_mp_op("Function 'f2ui()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::float2uint((float)mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((double)cimg::float2uint((float)mem[arg1])); _cimg_mp_scalar1(mp_f2ui,arg1); } if (!std::strncmp(ss,"fact(",5)) { // Factorial _cimg_mp_op("Function 'fact()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::factorial((int)mem[arg1])); _cimg_mp_scalar1(mp_factorial,arg1); } if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci _cimg_mp_op("Function 'fibo()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::fibonacci((int)mem[arg1])); _cimg_mp_scalar1(mp_fibonacci,arg1); } + if (!std::strncmp(ss,"fill(",5)) { // Fill + _cimg_mp_op("Function 'fill()'"); + s0 = ss5; while (s0::%s: %s: Target scalar is constant, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,ss); + s1 = ++s0; while (s1::%s: %s: Invalid loop variable name '%s', " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data,s0); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data,s0); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,block_flags); + _cimg_mp_check_type(arg3,3,1,0); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,block_flags); + } + // arg2 = variable slot, arg3 = fill expression. + _cimg_mp_check_type(arg3,3,1,0); + CImg::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1). + move_to(code,p1); + _cimg_mp_return_nan(); + } + if (!std::strncmp(ss,"find(",5)) { // Find _cimg_mp_op("Function 'find()'"); // First argument: data to look at. s0 = ss5; while (s0::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2, arg4 - arg3,code._width - arg4, - p3>=arg6 && !_cimg_mp_is_constant(p3), - p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + p3>=arg6 && !_cimg_mp_is_const_scalar(p3), + p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1); _cimg_mp_return(p3); } if (!std::strncmp(ss,"floor(",6)) { // Floor _cimg_mp_op("Function 'floor()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::floor(mem[arg1])); _cimg_mp_scalar1(mp_floor,arg1); } if (!std::strncmp(ss,"fsize(",6)) { // File size _cimg_mp_op("Function 'fsize()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); _cimg_mp_check_type(arg1,1,2,0); pos = scalar(); CImg::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } break; @@ -20096,20 +20642,20 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function _cimg_mp_op("Function 'gauss()'"); s1 = ss6; while (s1::max(); + if (mem[arg2]>=siz_max) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Specified variable size %g is larger than %d.", + pixel_type(),_cimg_mp_calling_function,s_op, + mem[arg2],siz_max); + } arg2 = (unsigned int)mem[arg2]; if (arg2) pos = vector(arg2); else pos = scalar(); CImg::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } #endif @@ -20153,12 +20709,10 @@ namespace cimg_library_suffixed { if (*ss1=='(') { // Image height _cimg_mp_op("Function 'h()'"); if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss3,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_h,pos,p1).move_to(code); - _cimg_mp_return(pos); + _cimg_mp_scalar1(mp_image_h,p1); } break; @@ -20166,33 +20720,24 @@ namespace cimg_library_suffixed { if (*ss1=='c' && *ss2=='(') { // Image median _cimg_mp_op("Function 'ic()'"); if (*ss3=='#') { // Index specified - p1 = compile(ss4,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); - } else { if (ss3!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_median,pos,p1).move_to(code); - _cimg_mp_return(pos); - } - - if (*ss1=='c' && *ss2=='(') { // Image median - _cimg_mp_op("Function 'ic()'"); - if (*ss3=='#') { // Index specified - p1 = compile(ss4,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss4,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss3!=se1) break; p1 = ~0U; } pos = scalar(); CImg::vector((ulongT)mp_image_median,pos,p1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (*ss1=='n' && *ss2=='(') { // Image norm _cimg_mp_op("Function 'in()'"); if (*ss3=='#') { // Index specified - p1 = compile(ss4,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss4,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss3!=se1) break; p1 = ~0U; } pos = scalar(); CImg::vector((ulongT)mp_image_norm,pos,p1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -20200,44 +20745,45 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'if()'"); s1 = ss3; while (s1::vector((ulongT)mp_if,pos,arg1,arg2,arg3, p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"inrange(",8)) { // Check value range _cimg_mp_op("Function 'inrange()'"); s1 = ss8; while (s1=val1) is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } - if (!std::strncmp(ss,"int(",4)) { // Integer cast _cimg_mp_op("Function 'int()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((longT)mem[arg1]); _cimg_mp_scalar1(mp_int,arg1); } - if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inversion + if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inverse (or pseudoinverse) _cimg_mp_op("Function 'invert()'"); s1 = ss7; while (s1::vector((ulongT)mp_matrix_invert,pos,arg1,p1,arg2).move_to(code); + p1 = _cimg_mp_size(arg1); + if (arg2==~0U) { // nb_colsA not specified: assuming square matrix + _cimg_mp_check_matrix_square(arg1,1); + p2 = p3 = (unsigned int)cimg::round(std::sqrt((float)p1)); + } else { + p2 = arg2; + p3 = p1/p2; + if (p3*p2!=p1) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of first argument ('%s') " + "does not match with second argument 'nb_colsA=%u', " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2,s0); + } + } + pos = vector(p1); + CImg::vector((ulongT)mp_matrix_invert,pos,arg1,p2,p3,arg3,arg4).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(1/mem[arg1]); _cimg_mp_scalar2(mp_div,1,arg1); } @@ -20293,28 +20872,28 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? _cimg_mp_op("Function 'isbool()'"); if (ss7==se1) _cimg_mp_return(0); - try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + try { arg1 = compile(ss7,se1,depth1,0,block_flags); } catch(CImgException&) { _cimg_mp_return(0); } if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.); _cimg_mp_scalar1(mp_isbool,arg1); } if (!std::strncmp(ss,"isdir(",6)) { // Is directory? _cimg_mp_op("Function 'isdir()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); - if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0,block_flags); pos = scalar(); CImg::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"isfile(",7)) { // Is file? _cimg_mp_op("Function 'isfile()'"); - arg1 = compile(ss7,se1,depth1,0,is_critical); - if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + arg1 = compile(ss7,se1,depth1,0,block_flags); pos = scalar(); CImg::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -20326,45 +20905,45 @@ namespace cimg_library_suffixed { for (s = ss5; s::sequence(_cimg_mp_size(arg1),arg1 + 1, - arg1 + (ulongT)_cimg_mp_size(arg1)). - move_to(l_opcode); - else CImg::vector(arg1).move_to(l_opcode); + arg1 = compile(s,ns,depth1,0,block_flags); + CImg::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); s = ns; } (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? _cimg_mp_op("Function 'isinf()'"); if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) + _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); _cimg_mp_scalar1(mp_isinf,arg1); } if (!std::strncmp(ss,"isint(",6)) { // Is integer? _cimg_mp_op("Function 'isint()'"); if (ss6==se1) _cimg_mp_return(0); - try { arg1 = compile(ss6,se1,depth1,0,is_critical); } + try { arg1 = compile(ss6,se1,depth1,0,block_flags); } catch(CImgException&) { _cimg_mp_return(0); } if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) + _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1])); _cimg_mp_scalar1(mp_isint,arg1); } if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? _cimg_mp_op("Function 'isnan()'"); if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) + _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); _cimg_mp_scalar1(mp_isnan,arg1); } @@ -20378,10 +20957,19 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression? _cimg_mp_op("Function 'isexpr()'"); if (ss7==se1) _cimg_mp_return(0); - try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + try { arg1 = compile(ss7,se1,depth1,0,block_flags); } catch (CImgException&) { _cimg_mp_return(0); } _cimg_mp_return(1); } + + if (!std::strncmp(ss,"isvarname(",10)) { // Is variable name? + _cimg_mp_op("Function 'isvarname()'"); + arg1 = compile(ss + 10,se1,depth1,0,block_flags); + pos = scalar(); + CImg::vector((ulongT)mp_isvarname,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } } break; @@ -20395,17 +20983,17 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation _cimg_mp_op("Function 'lerp()'"); s1 = ss5; while (s1::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"log(",4)) { // Natural logarithm _cimg_mp_op("Function 'log()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log(mem[arg1])); _cimg_mp_scalar1(mp_log,arg1); } if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm _cimg_mp_op("Function 'log2()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::log2(mem[arg1])); _cimg_mp_scalar1(mp_log2,arg1); } if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm _cimg_mp_op("Function 'log10()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log10(mem[arg1])); _cimg_mp_scalar1(mp_log10,arg1); } if (!std::strncmp(ss,"lowercase(",10)) { // Lower case _cimg_mp_op("Function 'lowercase()'"); - arg1 = compile(ss + 10,se1,depth1,0,is_critical); + arg1 = compile(ss + 10,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::lowercase(mem[arg1])); _cimg_mp_scalar1(mp_lowercase,arg1); } break; case 'm' : + if (!std::strncmp(ss,"map(",4)) { // Map vector + _cimg_mp_op("Function 'map()'"); + s1 = ss4; while (s1::%s: %s: Type of first arguments ('%s') " + "does not match with third argument 'nb_channelsX=%u', " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,arg3,s0); + } + if (p2%arg4) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of second arguments ('%s') " + "does not match with fourth argument 'nb_channelsP=%u', " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,arg4,s0); + } + pos = vector(p1*arg4); + CImg::vector((ulongT)mp_map,pos,arg1,arg2,p1,p2,arg3,arg4,arg5).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication _cimg_mp_op("Function 'mul()'"); s1 = ss4; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " "do not match with third argument 'nb_colsB=%u', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data,p3, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg1)._data,s_type(arg2)._data,p3,s0); } pos = vector(arg4*p3); CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary _cimg_mp_op("Function 'mproj()'"); s1 = ss6; while (s1::%s: %s: Type of first argument ('%s') " "do not match with second argument 'nb_colsS=%u', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,wS, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg1)._data,wS,s0); } if (wD*hD!=p2) { _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Type of third argument ('%s') " "do not match with fourth argument 'nb_colsD=%u', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg3)._data,wD, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg3)._data,wD,s0); } if (hS!=hD) { _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Type of first argument ('%s') " "do not match with third argument ('%s'), " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg3)._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg1)._data,s_type(arg3)._data,s0); } pos = vector(wS*wD); CImg::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } + if (!std::strncmp(ss,"mse(",4)) { // Mean-squared error + _cimg_mp_op("Function 'mse()'"); + s1 = ss4; while (s1::%s: %s: First argument cannot be a linked reference, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0); + } + + arg1 = ~0U; // Merge operator + // (0='=',1='+',2='-',3='*',4='/',5='&',6='|',7='xor',8='&&',9=='||',10='min',11='max') if (s1::%s: %s: Merge has already been requested before " "for specified variable " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s0); } if (arg1==~0U) { _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid specified operator " - "(should be one of '=,+,-,*,/,min,max'), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + "(should be one of '=,+,-,*,/,&,|,xor,&&,||,min,max'), " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s0); } memmerge.resize(3,memmerge._height + 1,1,1,0,0); memmerge(0,memmerge._height - 1) = (int)pos; memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos); memmerge(2,memmerge._height - 1) = (int)arg1; - _cimg_mp_return(pos); + _cimg_mp_return_nan(); } break; @@ -20603,17 +21264,18 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'name()'"); if (*ss5=='#') { // Index specified s0 = ss6; while (s0::vector((ulongT)mp_name,pos,p1,arg1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } #endif @@ -20627,7 +21289,7 @@ namespace cimg_library_suffixed { (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; ++arg1; s = ns; } - _cimg_mp_constant((double)arg1); + _cimg_mp_const_scalar((double)arg1); } if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') || @@ -20637,41 +21299,40 @@ namespace cimg_library_suffixed { if (*ss4=='(') { arg1 = 2; s = ss5; } else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; } else if (arg1==~0U) { - arg1 = compile(ss4,s++,depth1,0,is_critical); - _cimg_mp_check_constant(arg1,0,2); + arg1 = compile(ss4,s++,depth1,0,block_flags); + _cimg_mp_check_const_scalar(arg1,0,2); arg1 = (unsigned int)mem[arg1]; } else s = std::strchr(ss4,'(') + 1; + is_sth = true; // Tell if all arguments are constant pos = scalar(); switch (arg1) { - case 0 : - CImg::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break; - case 1 : - CImg::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break; - case 2 : - CImg::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break; - case ~0U : - CImg::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break; - default : - CImg::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). - move_to(l_opcode); + case 0 : op = mp_norm0; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; + case 1 : op = mp_norm1; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; + case 2 : op = mp_norm2; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; + case ~0U : op = mp_norminf; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; + default : op = mp_normp; CImg::vector((ulongT)op,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). + move_to(l_opcode); } for ( ; s::sequence(_cimg_mp_size(arg2),arg2 + 1, arg2 + (ulongT)_cimg_mp_size(arg2)). move_to(l_opcode); else CImg::vector(arg2).move_to(l_opcode); + is_sth&=_cimg_mp_is_const_scalar(arg2); s = ns; } (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + if (is_sth) _cimg_mp_const_scalar(op(*this)); if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 _cimg_mp_scalar1(mp_abs,opcode[3]); - opcode[2] = opcode._height; opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } break; @@ -20680,37 +21341,41 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"permut(",7)) { // Number of permutations _cimg_mp_op("Function 'permut()'"); s1 = ss7; while (s1::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); - for (s = s0; s1 && _cimg_mp_is_vector(arg2)) // Vector argument allowed to specify coordinates and color CImg::sequence(_cimg_mp_size(arg2),arg2 + 1, arg2 + (ulongT)_cimg_mp_size(arg2)). move_to(l_opcode); - else CImg::vector(arg2).move_to(l_opcode); + else { + _cimg_mp_check_type(arg2,pos,1,0); + CImg::vector(arg2).move_to(l_opcode); + } s = ns; } (l_opcode>'y').move_to(opcode); @@ -20719,92 +21384,79 @@ namespace cimg_library_suffixed { _cimg_mp_return_nan(); } - if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions - is_sth = ss[5]=='s'; // is prints() + if (!std::strncmp(ss,"print(",6) || + !std::strncmp(ss,"prints(",7)) { // Print expressions + s0 = ss6 + (*ss5=='('?0:1); + is_sth = *ss5=='s'; // corresponding string must be printed? _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'"); - s0 = is_sth?ss7:ss6; - if (*s0!='#' || is_sth) { // Regular expression - for (s = s0; s::string(s,true,true).unroll('y'),true); - cimg::strpare(variable_name,false,true); - if (_cimg_mp_is_vector(pos)) // Vector - ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0), - variable_name)>'y').move_to(opcode); - else // Scalar - ((CImg::vector((ulongT)mp_print,pos,0,is_sth?1:0), - variable_name)>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - *ns = c1; s = ns; - } - _cimg_mp_return(pos); - } else { // Image - p1 = compile(ss7,se1,depth1,0,is_critical); - _cimg_mp_check_list(true); + if (!is_sth && *s0=='#') { // Image + p1 = compile(ss7,se1,depth1,0,block_flags); + _cimg_mp_check_list(); CImg::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); _cimg_mp_return_nan(); } - } - if (!std::strncmp(ss,"pseudoinvert(",13)) { // Matrix/scalar pseudo-inversion - _cimg_mp_op("Function 'pseudoinvert()'"); - s1 = ss + 13; while (s1::string(s,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_const_scalar(pos)) // Const scalar + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g " + "(mem[%u]: %s%s)", + variable_name._data,mem[pos],pos,s_type(pos)._data,s_ref(ref)._data); + else // Vector or non-const scalar + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = (uninitialized) " + "(mem[%u]: %s%s)", + variable_name._data,pos,s_type(pos)._data,s_ref(ref)._data); + + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + + if (_cimg_mp_is_vector(pos)) // Vector + ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0), + variable_name)>'y').move_to(opcode); + else // Scalar + ((CImg::vector((ulongT)mp_print,pos,0,is_sth?1:0), + variable_name)>'y').move_to(opcode); + + opcode[2] = opcode._height; + opcode.move_to(code); + *ns = c1; s = ns; } - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_constant(arg2,2,3); - _cimg_mp_check_type(arg3,3,1,0); - p1 = _cimg_mp_size(arg1); - p2 = (unsigned int)mem[arg2]; - p3 = p1/p2; - if (p3*p2!=p1) { - _cimg_mp_strerr; - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Type of first argument ('%s') " - "does not match with second argument 'nb_colsA=%u', " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,p2, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(p1); - CImg::vector((ulongT)mp_matrix_pseudoinvert,pos,arg1,p2,p3,arg3).move_to(code); _cimg_mp_return(pos); } break; case 'r' : + if (!std::strncmp(ss,"rad2deg(",8)) { // Degrees to radians + _cimg_mp_op("Function 'rad2deg()'"); + arg1 = compile(ss8,se1,depth1,0,block_flags); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_rad2deg,arg1); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*180/cimg::PI); + _cimg_mp_scalar1(mp_rad2deg,arg1); + } + if (!std::strncmp(ss,"ref(",4)) { // Variable declaration _cimg_mp_op("Function 'ref()'"); s1 = ss4; while (s1=se1 || !*s1) compile(s1,s1,depth1,0,is_critical); // Will throw missing argument error - arg3 = compile(ss4,s1++,depth1,p_ref,is_critical); + if (s1>=se1 || !*s1) compile(s1,s1,depth1,0,block_flags); // Will throw missing argument error + arg3 = compile(ss4,s1++,depth1,p_ref,block_flags); *se1 = 0; - is_sth = true; - if (*s1>='0' && *s1<='9') is_sth = false; - else for (ns = s1; *ns; ++ns) if (!is_varchar(*ns)) { is_sth = false; break; } - if (!is_sth) { + + if (!cimg::is_varname(s1)) { // Invalid variable name variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0; cimg::strellipsize(variable_name,64); *se1 = ')'; _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid specified variable name '%s', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); - + variable_name._data,s0); } get_variable_pos(s1,arg1,arg2); if (arg2!=~0U) reserved_label[arg2] = arg3; @@ -20815,40 +21467,136 @@ namespace cimg_library_suffixed { CImg::string(s1).move_to(variable_def); } if (_cimg_mp_is_vector(arg3)) - set_variable_vector(arg3); // Prevent from being used in further optimization + set_reserved_vector(arg3); // Prevent from being used in further optimization else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; *se1 = ')'; _cimg_mp_return(arg3); } + if (!std::strncmp(ss,"repeat(",7)) { // Repeat + _cimg_mp_op("Function 'repeat()'"); + s0 = ss7; while (s0::%s: %s: Invalid loop variable name '%s', " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data,s0); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data,s0); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,block_flags); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,block_flags); + } + // arg2 = variable slot, arg3 = fill expression. + CImg::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1); + _cimg_mp_return_nan(); + } + if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize _cimg_mp_op("Function 'resize()'"); if (*ss7!='#') { // Vector - s1 = ss7; while (s1::vector(arg2).move_to(l_opcode); + s = ns; } - _cimg_mp_check_constant(arg2,2,3); - arg2 = (unsigned int)mem[arg2]; - _cimg_mp_check_type(arg3,3,1,0); - _cimg_mp_check_type(arg4,4,1,0); - pos = vector(arg2); - CImg::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1), - arg3,arg4).move_to(code); + (l_opcode>'y').move_to(opcode); + if (opcode.height()<2) compile(s,se1,depth1,0,block_flags); // Not enough arguments -> throw exception + arg1 = (unsigned int)opcode[0]; // Vector to resize + p1 = _cimg_mp_size(arg1); + + if (opcode.height()<=4) { // Simple vector resize + arg2 = (unsigned int)opcode[1]; + _cimg_mp_check_const_scalar(arg2,2,3); + arg2 = (unsigned int)mem[arg2]; + arg3 = opcode.height()<3?1U:(unsigned int)opcode[2]; + _cimg_mp_check_type(arg3,3,1,0); + arg4 = opcode.height()<4?0U:(unsigned int)opcode[3]; + _cimg_mp_check_type(arg4,4,1,0); + pos = vector(arg2); + CImg::vector((ulongT)mp_vector_resize,pos,arg2,arg1,p1,arg3,arg4).move_to(code); + } else { // Advanced vector resize (vector viewed as an image) + // opcode = [ A, ow,oh,od,os, nw,nh,nd,ns, interp, boundary_cond, ax,ay,az,ac ] + // [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ] + + if (opcode.height()<6) compile(s,se1,depth1,0,block_flags); // Not enough arguments -> throw exception + p2 = opcode.height(); + opcode.resize(1,15,1,1,0); + if (p2<7) opcode[6] = opcode[2]; + if (p2<8) opcode[7] = opcode[3]; + if (p2<9) opcode[8] = opcode[4]; + if (p2<10) opcode[9] = 1; + _cimg_mp_check_const_scalar(opcode[1],2,3); + _cimg_mp_check_const_scalar(opcode[2],3,3); + _cimg_mp_check_const_scalar(opcode[3],4,3); + _cimg_mp_check_const_scalar(opcode[4],5,3); + _cimg_mp_check_const_scalar(opcode[5],6,3); + _cimg_mp_check_const_scalar(opcode[6],7,3); + _cimg_mp_check_const_scalar(opcode[7],8,3); + _cimg_mp_check_const_scalar(opcode[8],9,3); + arg2 = (unsigned int)mem[opcode[1]]; opcode[1] = arg2; + arg3 = (unsigned int)mem[opcode[2]]; opcode[2] = arg3; + arg4 = (unsigned int)mem[opcode[3]]; opcode[3] = arg4; + arg5 = (unsigned int)mem[opcode[4]]; opcode[4] = arg5; + if (arg2*arg3*arg4*arg5!=std::max(1U,p1)) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Input vector size (%lu values) and its specified " + "geometry (%u,%u,%u,%u) (%lu values) do not match.", + pixel_type(),_cimg_mp_calling_function,s_op, + std::max(p1,1U),arg2,arg3,arg4,arg5,(ulongT)arg2*arg3*arg4*arg5); + arg2 = (unsigned int)mem[opcode[5]]; opcode[5] = arg2; + arg3 = (unsigned int)mem[opcode[6]]; opcode[6] = arg3; + arg4 = (unsigned int)mem[opcode[7]]; opcode[7] = arg4; + arg5 = (unsigned int)mem[opcode[8]]; opcode[8] = arg5; + pos = vector(arg2*arg3*arg4*arg5); + opcode.resize(1,18,1,1,0,0,0,1); + opcode[0] = (ulongT)mp_vector_resize_ext; + opcode[1] = (ulongT)pos; + opcode[2] = (ulongT)p1; + opcode.move_to(code); + } + return_new_comp = true; _cimg_mp_return(pos); } else { // Image - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; s0 = ss8; while (s0::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0). move_to(l_opcode); @@ -20856,7 +21604,7 @@ namespace cimg_library_suffixed { for (s = s0; s10) { _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.", + "CImg<%s>::%s: %s: %s arguments, in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - pos<1?"Missing":"Too much", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + pos<1?"Missing":"Too much",s0); } l_opcode[0].move_to(code); _cimg_mp_return_nan(); @@ -20877,23 +21624,24 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse _cimg_mp_op("Function 'reverse()'"); - arg1 = compile(ss8,se1,depth1,0,is_critical); + arg1 = compile(ss8,se1,depth1,0,block_flags); if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1); p1 = _cimg_mp_size(arg1); pos = vector(p1); CImg::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'"); s1 = ss4; while (s12) arg3 = arg2 + 1; } - arg4 = compile(++s1,se1,depth1,0,is_critical); + arg4 = compile(++s1,se1,depth1,0,block_flags); } else { - s2 = s1 + 1; while (s2::vector((ulongT)mp_rot2d,pos,arg1).move_to(code); } + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"round(",6)) { // Value rounding _cimg_mp_op("Function 'round()'"); s1 = ss6; while (s1::vector((ulongT)mp_run,0,0).move_to(l_opcode); pos = 1; for (s = ss4; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); s = ns; } @@ -20972,6 +21724,7 @@ namespace cimg_library_suffixed { opcode[1] = pos; opcode[2] = opcode._height; opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } #endif @@ -20981,42 +21734,54 @@ namespace cimg_library_suffixed { if (*ss1=='(') { // Image spectrum _cimg_mp_op("Function 's()'"); if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss3,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_s,pos,p1).move_to(code); - _cimg_mp_return(pos); + _cimg_mp_scalar1(mp_image_s,p1); } if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values _cimg_mp_op("Function 'same()'"); s1 = ss5; while (s1::vector((ulongT)mp_set,arg2,p2,arg1,p1).move_to(code); + _cimg_mp_return_nan(); + } +#endif + if (!std::strncmp(ss,"shift(",6)) { // Shift vector _cimg_mp_op("Function 'shift()'"); s1 = ss6; while (s1::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"sign(",5)) { // Sign _cimg_mp_op("Function 'sign()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sign(mem[arg1])); _cimg_mp_scalar1(mp_sign,arg1); } if (!std::strncmp(ss,"sin(",4)) { // Sine _cimg_mp_op("Function 'sin()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sin(mem[arg1])); _cimg_mp_scalar1(mp_sin,arg1); } if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal _cimg_mp_op("Function 'sinc()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sinc(mem[arg1])); _cimg_mp_scalar1(mp_sinc,arg1); } if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine _cimg_mp_op("Function 'sinh()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sinh(mem[arg1])); _cimg_mp_scalar1(mp_sinh,arg1); } if (!std::strncmp(ss,"size(",5)) { // Vector size _cimg_mp_op("Function 'size()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); - _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1)); + arg1 = compile(ss5,se1,depth1,0,block_flags); + _cimg_mp_const_scalar(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1)); } if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system _cimg_mp_op("Function 'solve()'"); s1 = ss6; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " "do not match with third argument 'nb_colsB=%u', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,s_type(arg2)._data,p3, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg1)._data,s_type(arg2)._data,p3,s0); } - pos = vector(arg4*p3); - CImg::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + pos = vector(arg6*p3); + CImg::vector((ulongT)mp_solve,pos,arg1,arg2,arg6,arg5,p3,arg4).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"sort(",5)) { // Sort vector _cimg_mp_op("Function 'sort()'"); s1 = ss5; while (s1::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"sqr(",4)) { // Square _cimg_mp_op("Function 'sqr()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sqr(mem[arg1])); _cimg_mp_scalar1(mp_sqr,arg1); } if (!std::strncmp(ss,"sqrt(",5)) { // Square root _cimg_mp_op("Function 'sqrt()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sqrt(mem[arg1])); _cimg_mp_scalar1(mp_sqrt,arg1); } if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed _cimg_mp_op("Function 'srand()'"); - arg1 = ss6::vector((ulongT)mp_image_stats,pos,p1).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -21157,78 +21933,61 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"store(",6)) { // Store vector to variable _cimg_mp_op("Function 'store()'"); s1 = ss6; while (s1p3) { - _cimg_mp_strerr; - throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s: Specified dimensions (%u,%u,%u,%u) " - "are too large for vector size (%u), " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - varg3,varg4,varg5,varg6,p3, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - } - CImg::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg1,p1,arg2,p2, + if (arg3==~0U) arg3 = const_scalar(p3); + CImg::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg2,p2,arg1,p1, arg3,arg4,arg5,arg6,pos).move_to(code); _cimg_mp_return_nan(); } #endif - if (!std::strncmp(ss,"stov(",5)) { // String to double - _cimg_mp_op("Function 'stov()'"); - s1 = ss5; while (s1::vector((ulongT)mp_stov,pos,arg1,p1,arg2,arg3).move_to(code); + CImg::vector((ulongT)mp_s2v,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -21238,8 +21997,8 @@ namespace cimg_library_suffixed { if (*ss7=='#') { // Output vector size specified, with '#' s0 = ss8; while (s0::vector(arg2,p2).move_to(l_opcode); @@ -21265,16 +22024,17 @@ namespace cimg_library_suffixed { opcode[2] = arg1; opcode[3] = opcode._height; opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD _cimg_mp_op("Function 'svd()'"); s1 = ss4; while (s1::%s: %s: Type of first argument ('%s') " "does not match with second argument 'nb_colsA=%u', " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,p2, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg1)._data,p2,s0); } pos = vector(p1 + p2 + p2*p2); CImg::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -21297,18 +22057,17 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'swap()'"); s1 = ss5; while (s1::%s: %s: %s argument cannot be a constant, " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - _cimg_mp_is_constant(arg1)?"First":"Second", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + _cimg_mp_is_const_scalar(arg1)?"First":"Second",s0); } CImg::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code); @@ -21324,12 +22083,12 @@ namespace cimg_library_suffixed { move_to(code); break; case 2 : // arg1: i/j[_#ind,off] - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = _ref[1]; // Index is_relative = (bool)_ref[2]; arg3 = _ref[3]; // Offset if (p1!=~0U) { - if (listout) + if (imglist) CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), arg1,p1,arg3).move_to(code); } else { @@ -21339,7 +22098,7 @@ namespace cimg_library_suffixed { } break; case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c) - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = _ref[1]; // Index is_relative = (bool)_ref[2]; arg3 = _ref[3]; // X @@ -21347,7 +22106,7 @@ namespace cimg_library_suffixed { arg5 = _ref[5]; // Z arg6 = _ref[6]; // C if (p1!=~0U) { - if (listout) + if (imglist) CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), arg1,p1,arg3,arg4,arg5,arg6).move_to(code); } else { @@ -21357,17 +22116,17 @@ namespace cimg_library_suffixed { } break; case 4: // arg1: I/J[_#ind,off] - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = _ref[1]; // Index is_relative = (bool)_ref[2]; arg3 = _ref[3]; // Offset if (p1!=~0U) { - if (listout) { + if (imglist) { if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), arg1,p1,arg3).move_to(code); else { - _cimg_mp_check_constant_index(p1); + _cimg_mp_check_const_index(p1); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); } @@ -21384,19 +22143,19 @@ namespace cimg_library_suffixed { } break; case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c) - if (!is_critical) is_parallelizable = false; + if (!is_inside_critical) is_parallelizable = false; p1 = _ref[1]; // Index is_relative = (bool)_ref[2]; arg3 = _ref[3]; // X arg4 = _ref[4]; // Y arg5 = _ref[5]; // Z if (p1!=~0U) { - if (listout) { + if (imglist) { if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), arg1,p1,arg3,arg4,arg5).move_to(code); else { - _cimg_mp_check_constant_index(p1); + _cimg_mp_check_const_index(p1); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } @@ -21420,30 +22179,30 @@ namespace cimg_library_suffixed { } while (!is_sth); if (p_ref) std::memcpy(p_ref,ref,siz_ref); - _cimg_mp_return(arg1); + _cimg_mp_return_nan(); } break; case 't' : if (!std::strncmp(ss,"tan(",4)) { // Tangent _cimg_mp_op("Function 'tan()'"); - arg1 = compile(ss4,se1,depth1,0,is_critical); + arg1 = compile(ss4,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tan(mem[arg1])); _cimg_mp_scalar1(mp_tan,arg1); } if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent _cimg_mp_op("Function 'tanh()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tanh(mem[arg1])); _cimg_mp_scalar1(mp_tanh,arg1); } if (!std::strncmp(ss,"trace(",6)) { // Matrix trace _cimg_mp_op("Function 'trace()'"); - arg1 = compile(ss6,se1,depth1,0,is_critical); + arg1 = compile(ss6,se1,depth1,0,block_flags); _cimg_mp_check_matrix_square(arg1,1); p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); _cimg_mp_scalar2(mp_trace,arg1,p1); @@ -21452,10 +22211,10 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose _cimg_mp_op("Function 'transpose()'"); s1 = ss + 10; while (s1::%s: %s: Size of first argument ('%s') does not match " - "second argument 'nb_cols=%u', in expression '%s%s%s'.", + "second argument 'nb_cols=%u', in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - s_type(arg1)._data,p2, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg1)._data,p2,s0); } pos = vector(p3*p2); CImg::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } break; case 'u' : - if (*ss1=='(') { // Random value with uniform distribution + if (*ss1=='(') { // Random value with uniform distribution in specified range _cimg_mp_op("Function 'u()'"); if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); s1 = ss2; while (s1float conversion _cimg_mp_op("Function 'ui2f()'"); - arg1 = compile(ss5,se1,depth1,0,is_critical); + arg1 = compile(ss5,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::uint2float((unsigned int)mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) + _cimg_mp_const_scalar((double)cimg::uint2float((unsigned int)mem[arg1])); _cimg_mp_scalar1(mp_ui2f,arg1); } @@ -21515,16 +22299,16 @@ namespace cimg_library_suffixed { --variable_pos._width; } *s1 = c1; - } else compile(s0,s1,depth1,0,is_critical); // Will throw a 'missing argument' exception + } else compile(s0,s1,depth1,0,block_flags); // Will throw a 'missing argument' exception } _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable } if (!std::strncmp(ss,"uppercase(",10)) { // Upper case _cimg_mp_op("Function 'uppercase()'"); - arg1 = compile(ss + 10,se1,depth1,0,is_critical); + arg1 = compile(ss + 10,se1,depth1,0,block_flags); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::uppercase(mem[arg1])); + if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::uppercase(mem[arg1])); _cimg_mp_scalar1(mp_uppercase,arg1); } break; @@ -21536,15 +22320,15 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'vector()'"); arg2 = 0; // Number of specified values if (arg1==~0U && *ss6!='(') { - arg1 = compile(ss6,s++,depth1,0,is_critical); - _cimg_mp_check_constant(arg1,0,3); + arg1 = compile(ss6,s++,depth1,0,block_flags); + _cimg_mp_check_const_scalar(arg1,0,3); arg1 = (unsigned int)mem[arg1]; } else s = std::strchr(ss6,'(') + 1; if (arg1==~0U && *s=='#') { // Number of elements specified as first argument with '#' s0 = ++s; while (s0::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode); @@ -21567,6 +22351,7 @@ namespace cimg_library_suffixed { (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -21612,7 +22397,7 @@ namespace cimg_library_suffixed { for (s = std::strchr(ss,'(') + 1; s::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code); + CImg::vector((ulongT)mp_v2s,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } break; @@ -21657,45 +22444,37 @@ namespace cimg_library_suffixed { if (*ss1=='(') { // Image width _cimg_mp_op("Function 'w()'"); if (*ss2=='#') { // Index specified - p1 = compile(ss3,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss3,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss2!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_w,pos,p1).move_to(code); - _cimg_mp_return(pos); + _cimg_mp_scalar1(mp_image_w,p1); } if (*ss1=='h' && *ss2=='(') { // Image width*height _cimg_mp_op("Function 'wh()'"); if (*ss3=='#') { // Index specified - p1 = compile(ss4,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss4,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss3!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_wh,pos,p1).move_to(code); - _cimg_mp_return(pos); + _cimg_mp_scalar1(mp_image_wh,p1); } if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth _cimg_mp_op("Function 'whd()'"); if (*ss4=='#') { // Index specified - p1 = compile(ss5,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss5,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss4!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_whd,pos,p1).move_to(code); - _cimg_mp_return(pos); + _cimg_mp_scalar1(mp_image_whd,p1); } if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum _cimg_mp_op("Function 'whds()'"); if (*ss5=='#') { // Index specified - p1 = compile(ss6,se1,depth1,0,is_critical); - _cimg_mp_check_list(false); + p1 = compile(ss6,se1,depth1,0,block_flags); + _cimg_mp_check_list(); } else { if (ss5!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)mp_image_whds,pos,p1).move_to(code); - _cimg_mp_return(pos); + _cimg_mp_scalar1(mp_image_whds,p1); } if (!std::strncmp(ss,"while(",6)) { // While...do @@ -21703,15 +22482,15 @@ namespace cimg_library_suffixed { s0 = *ss5=='('?ss6:ss8; s1 = s0; while (s1::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2, - pos>=arg6 && !_cimg_mp_is_constant(pos), - arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1); + pos>=arg6 && !_cimg_mp_is_const_scalar(pos), + arg1>=arg6 && !_cimg_mp_is_const_scalar(arg1)).move_to(code,p1); _cimg_mp_return(pos); } break; @@ -21720,14 +22499,14 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"xor(",4)) { // Xor _cimg_mp_op("Function 'xor()'"); s1 = ss4; while (s1::sequence(_cimg_mp_size(arg2),arg2 + 1, arg2 + (ulongT)_cimg_mp_size(arg2)). move_to(l_opcode); else CImg::vector(arg2).move_to(l_opcode); - is_sth&=_cimg_mp_is_constant(arg2); + is_sth&=_cimg_mp_is_const_scalar(arg2); s = ns; } (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; - if (is_sth) _cimg_mp_constant(op(*this)); + if (is_sth) _cimg_mp_const_scalar(op(*this)); opcode.move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -21805,28 +22585,40 @@ namespace cimg_library_suffixed { (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns; } + char mb = 0; arg3 = 0; // Number of possible name matches cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 && - macro_def[l].back()==(char)p1) { - p2 = (unsigned int)macro_def[l].back(); // Number of required arguments + ((mb = macro_def[l].back())==(char)p1 || mb==(char)-1)) { + const bool is_variadic = mb==(char)-1; + p2 = is_variadic?1U:(unsigned int)mb; // Number of required arguments CImg _expr = macro_body[l]; // Expression to be substituted p1 = 1; // Index of current parsed argument for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments while (*s && cimg::is_blank(*s)) ++s; - if (*s==')' && p1==1) break; // Function has no arguments + if (!is_variadic && *s==')' && p1==1) break; // Function has no arguments if (p1>p2) { ++p1; break; } - ns = s; while (ns1) { + _expr.resize(arg1 + variable_name._width - 2,1,1,1,0); + std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1); + std::memcpy(_expr._data + k,variable_name,variable_name._width - 1); + k+=variable_name._width - 2; + } else { + std::memmove(_expr._data + k,_expr._data + k + 1,arg1 - k - 1); + --k; + } } ++arg2; } @@ -21847,7 +22639,7 @@ namespace cimg_library_suffixed { level.swap(_level); s0 = user_macro; user_macro = macro_def[l]; - pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_critical); + pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,block_flags); user_macro = s0; level.swap(_level); pexpr.swap(_pexpr); @@ -21869,18 +22661,16 @@ namespace cimg_library_suffixed { throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " "does not match macro declaration (defined for %s or %u arguments), " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,variable_name._data, - p1,sig_nargs.value_string()._data,arg1, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + p1,sig_nargs.value_string()._data,arg1,s0); } else throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " "does not match macro declaration (defined for %u argument%s), " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,variable_name._data, - p1,*sig_nargs,*sig_nargs!=1?"s":"", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + p1,*sig_nargs,*sig_nargs!=1?"s":"",s0); } } } // if (se1==')') @@ -21899,21 +22689,21 @@ namespace cimg_library_suffixed { } if (!arg1) _cimg_mp_return(0); // Empty string -> 0 if (*ss=='_') { - if (arg1==1) _cimg_mp_constant((unsigned char)*variable_name); + if (arg1==1) _cimg_mp_const_scalar((unsigned char)*variable_name); _cimg_mp_strerr; cimg::strellipsize(variable_name,64); throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Literal %s contains more than one byte, " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op, - ss1, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + ss1,s0); } pos = vector(arg1); CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); (l_opcode>'y').move_to(code); + return_new_comp = true; _cimg_mp_return(pos); } @@ -21940,7 +22730,7 @@ namespace cimg_library_suffixed { if (*ss1!=']') for (s = ss1; s::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode); @@ -21955,96 +22745,100 @@ namespace cimg_library_suffixed { opcode[2] = opcode._height; opcode.move_to(code); } + return_new_comp = true; _cimg_mp_return(pos); } // Variables related to the input list of images. if (*ss1=='#' && ss2::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code); + return_new_comp = true; _cimg_mp_return(pos); case 'R' : // R#ind - if (!listin) _cimg_mp_return(0); + if (!imglist) _cimg_mp_return(0); _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0, 0,_cimg_mp_boundary); case 'G' : // G#ind - if (!listin) _cimg_mp_return(0); + if (!imglist) _cimg_mp_return(0); _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1, 0,_cimg_mp_boundary); case 'B' : // B#ind - if (!listin) _cimg_mp_return(0); + if (!imglist) _cimg_mp_return(0); _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2, 0,_cimg_mp_boundary); case 'A' : // A#ind - if (!listin) _cimg_mp_return(0); + if (!imglist) _cimg_mp_return(0); _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3, 0,_cimg_mp_boundary); } } if (*ss1 && *ss2=='#' && ss3='0' && *ss1<='9') { // i0#ind...i9#ind - if (!listin) _cimg_mp_return(0); + if (!imglist) _cimg_mp_return(0); _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0', 0,_cimg_mp_boundary); } if (*ss1=='c') { // ic#ind - if (!listin) _cimg_mp_return(0); - if (_cimg_mp_is_constant(arg1)) { - if (!list_median) list_median.assign(listin._width); - if (!list_median[p1]) CImg::vector(listin[p1].median()).move_to(list_median[p1]); - _cimg_mp_constant(*list_median[p1]); + if (!imglist) _cimg_mp_return(0); + if (_cimg_mp_is_const_scalar(arg1)) { + if (!list_median) list_median.assign(imglist._width); + if (!list_median[p1]) CImg::vector(imglist[p1].median()).move_to(list_median[p1]); + _cimg_mp_const_scalar(*list_median[p1]); } _cimg_mp_scalar1(mp_list_median,arg1); } if (*ss1=='n') { // in#ind - if (!listin) _cimg_mp_return(0); - if (_cimg_mp_is_constant(arg1)) { - if (!list_norm) list_norm.assign(listin._width); - if (!list_norm[p1]) CImg::vector(listin[p1].magnitude()).move_to(list_norm[p1]); - _cimg_mp_constant(*list_norm[p1]); + if (!imglist) _cimg_mp_return(0); + if (_cimg_mp_is_const_scalar(arg1)) { + if (!list_norm) list_norm.assign(imglist._width); + if (!list_norm[p1]) CImg::vector(imglist[p1].magnitude()).move_to(list_norm[p1]); + _cimg_mp_const_scalar(*list_norm[p1]); } _cimg_mp_scalar1(mp_list_norm,arg1); } @@ -22069,41 +22863,44 @@ namespace cimg_library_suffixed { case 'c' : arg2 = 11; break; // cM#ind } if (arg2!=~0U) { - if (!listin) _cimg_mp_return(0); - if (_cimg_mp_is_constant(arg1)) { - if (!list_stats) list_stats.assign(listin._width); - if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false); - _cimg_mp_constant(list_stats(p1,arg2)); + if (!imglist) _cimg_mp_return(0); + if (_cimg_mp_is_const_scalar(arg1)) { + if (!list_stats) list_stats.assign(imglist._width); + if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false); + _cimg_mp_const_scalar(list_stats(p1,arg2)); } _cimg_mp_scalar2(mp_list_stats,arg1,arg2); } } if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4='0' && *variable_name<='9') is_sth = false; - else for (ns = variable_name; *ns; ++ns) - if (!is_varchar(*ns)) { is_sth = false; break; } - if (is_sth) { + +#ifdef cimg_mp_operator_dollar + if (*ss=='$' && ss1::%s: Undefined variable '%s' in expression '%s%s%s'.", + "CImg<%s>::%s: Undefined variable '%s' in expression '%s'.", pixel_type(),_cimg_mp_calling_function, - variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + variable_name._data,s0); s1 = std::strchr(ss,'('); s_op = s1 && c1==')'?"function call":"item"; throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", + "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s'.", pixel_type(),_cimg_mp_calling_function, - s_op,variable_name._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_op,variable_name._data,s0); } // Evaluation procedure. @@ -22158,12 +22953,7 @@ namespace cimg_library_suffixed { // Evaluation procedure for begin_t() bloc. void begin_t() { if (!code_begin_t) return; - if (imgin) { - mem[_cimg_mp_slot_x] = imgin._width - 1.; - mem[_cimg_mp_slot_y] = imgin._height - 1.; - mem[_cimg_mp_slot_z] = imgin._depth - 1.; - mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; - } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; p_code_end = code_begin_t.end(); for (p_code = code_begin_t; p_code_data; @@ -22217,33 +23007,59 @@ namespace cimg_library_suffixed { siz = (unsigned int)mp.memmerge(1,k), iop = (unsigned int)mp.memmerge(2,k); if (!siz) switch (iop) { // Scalar value - case 0 : mp.mem[pos] = mem[pos]; break; // Assignment - case 1 : mp.mem[pos]+=mem[pos]; break; // Operator+ - case 2 : mp.mem[pos]-=mem[pos]; break; // Operator- - case 3 : mp.mem[pos]*=mem[pos]; break; // Operator* - case 4 : mp.mem[pos]/=mem[pos]; break; // Operator/ - case 5 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min' - case 6 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max' + case 0 : mp.mem[pos] = mem[pos]; break; // Assignment + case 1 : mp.mem[pos]+=mem[pos]; break; // Operator+ + case 2 : mp.mem[pos]-=mem[pos]; break; // Operator- + case 3 : mp.mem[pos]*=mem[pos]; break; // Operator* + case 4 : mp.mem[pos]/=mem[pos]; break; // Operator/ + case 5 : mp.mem[pos] = (double)((longT)mp.mem[pos] & (longT)mem[pos]); break; // Operator& + case 6 : mp.mem[pos] = (double)((longT)mp.mem[pos] | (longT)mem[pos]); break; // Operator| + case 7 : mp.mem[pos] = (double)((longT)mp.mem[pos] ^ (longT)mem[pos]); break; // Operator 'xor' + case 8 : mp.mem[pos] = mp.mem[pos] && mem[pos]; break; // Operator&& + case 9 : mp.mem[pos] = mp.mem[pos] || mem[pos]; break; // Operator|| + case 10 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min' + case 11 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max' } else switch (iop) { // Vector value - case 0 : + case 0 : // Assignment CImg(&mp.mem[pos + 1],siz,1,1,1,true) = CImg(&mem[pos + 1],siz,1,1,1,true); break; - case 1 : + case 1 : // Operator+ CImg(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg(&mem[pos + 1],siz,1,1,1,true); break; - case 2 : + case 2 : // Operator- CImg(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg(&mem[pos + 1],siz,1,1,1,true); break; - case 3 : + case 3 : // Operator* CImg(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg(&mem[pos + 1],siz,1,1,1,true); break; - case 4 : + case 4 : // Operator/ CImg(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg(&mem[pos + 1],siz,1,1,1,true); break; - case 5 : + case 5 : // Operator& + CImg(&mp.mem[pos + 1],siz,1,1,1,true)&=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 6 : // Operator| + CImg(&mp.mem[pos + 1],siz,1,1,1,true)|=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 7 : // Operator 'xor' + CImg(&mp.mem[pos + 1],siz,1,1,1,true)^=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 8 : { // Operator&& + CImg + arg1(&mp.mem[pos + 1],siz,1,1,1,true), + arg2(&mem[pos + 1],siz,1,1,1,true); + cimg_foroff(arg1,off) arg1[off] = arg1[off] && arg2[off]; + } break; + case 9 : { // Operator|| + CImg + arg1(&mp.mem[pos + 1],siz,1,1,1,true), + arg2(&mem[pos + 1],siz,1,1,1,true); + cimg_foroff(arg1,off) arg1[off] = arg1[off] || arg2[off]; + } break; + case 10 : // Operator 'min' CImg(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg(&mem[pos + 1],siz,1,1,1,true)); break; - case 6 : + case 11 : // Operator 'max' CImg(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg(&mem[pos + 1],siz,1,1,1,true)); break; } @@ -22275,13 +23091,60 @@ namespace cimg_library_suffixed { return res; } - // Return type of a memory element as a string. + // Return type of a memory slot as a string. CImg s_type(const unsigned int arg) const { CImg res; if (_cimg_mp_is_vector(arg)) { // Vector CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); - cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg)); - } else CImg::string("scalar").move_to(res); + cimg_snprintf(res._data + 6,res._width - 6,"%u",_cimg_mp_size(arg)); + } else if (_cimg_mp_is_const_scalar(arg)) CImg::string("const scalar").move_to(res); // Const scalar + else CImg::string("scalar").move_to(res); // Scalar + return res; + } + + // Return reference state of a memory slot as a string. + CImg s_ref(const unsigned int *const p_ref) const { + CImg res; + if (!p_ref || !*p_ref) return res.assign(1,1,1,1,0); + res.assign(32); + switch (p_ref[0]) { + case 1 : // Reference to vector value as a scalar + cimg_snprintf(res,res._width,", ref: ([%u])[%u]", + p_ref[1],p_ref[2]); + break; + case 2 : // Reference to image value as a scalar (offset) + if (p_ref[1]==~0U) + cimg_snprintf(res,res._width,", ref: %c[%u]", + p_ref[2]?'j':'i',p_ref[3]); + else + cimg_snprintf(res,res._width,", ref: %c[#%u,%u]", + p_ref[2]?'j':'i',p_ref[1],p_ref[3]); + break; + case 3 : // Reference to image value as a scalar (coordinates) + if (p_ref[1]==~0U) + cimg_snprintf(res,res._width,", ref: %c(%u,%u,%u,%u)", + p_ref[2]?'j':'i',p_ref[3],p_ref[4],p_ref[5],p_ref[6]); + else + cimg_snprintf(res,res._width,", ref: %c(#%u,%u,%u,%u,%u)", + p_ref[2]?'j':'i',p_ref[1],p_ref[3],p_ref[4],p_ref[5],p_ref[6]); + break; + case 4 : // Reference to image value as a vector (offset) + if (p_ref[1]==~0U) + cimg_snprintf(res,res._width,", ref: %c[%u]", + p_ref[2]?'J':'I',p_ref[3]); + else + cimg_snprintf(res,res._width,", ref: %c[#%u,%u]", + p_ref[2]?'J':'I',p_ref[1],p_ref[3]); + break; + case 5 : // Reference to image value as a vector (coordinates) + if (p_ref[1]==~0U) + cimg_snprintf(res,res._width,", ref: %c(%u,%u,%u)", + p_ref[2]?'J':'I',p_ref[3],p_ref[4],p_ref[5]); + else + cimg_snprintf(res,res._width,", ref: %c(#%u,%u,%u,%u)", + p_ref[2]?'J':'I',p_ref[1],p_ref[3],p_ref[4],p_ref[5]); + break; + } return res; } @@ -22324,16 +23187,16 @@ namespace cimg_library_suffixed { return res; } - // Find and return index of current image 'imgin' within image list 'listin'. + // Find and return index of current image 'imgin' within image list 'imglist'. unsigned int get_mem_img_index() { if (mem_img_index==~0U) { - if (&imgout>listout.data() && &imgoutimglist.data() && &imgout='0' && c2<='9') rp = 20 + c2 - '0'; // i0...i9 + if (c2>='0' && c2<='9') rp = 21 + c2 - '0'; // i0...i9 else if (c2=='m') rp = 4; // im else if (c2=='M') rp = 5; // iM else if (c2=='a') rp = 6; // ia else if (c2=='v') rp = 7; // iv - else if (c2=='s') rp = 8; // is - else if (c2=='p') rp = 9; // ip - else if (c2=='c') rp = 10; // ic - else if (c2=='n') rp = 11; // in + else if (c2=='d') rp = 8; // id + else if (c2=='s') rp = 9; // is + else if (c2=='p') rp = 10; // ip + else if (c2=='c') rp = 11; // ic + else if (c2=='n') rp = 12; // in } else if (c2=='m') { - if (c1=='x') rp = 12; // xm - else if (c1=='y') rp = 13; // ym - else if (c1=='z') rp = 14; // zm - else if (c1=='c') rp = 15; // cm + if (c1=='x') rp = 13; // xm + else if (c1=='y') rp = 14; // ym + else if (c1=='z') rp = 15; // zm + else if (c1=='c') rp = 16; // cm } else if (c2=='M') { - if (c1=='x') rp = 16; // xM - else if (c1=='y') rp = 17; // yM - else if (c1=='z') rp = 18; // zM - else if (c1=='c') rp = 19; // cM + if (c1=='x') rp = 17; // xM + else if (c1=='y') rp = 18; // yM + else if (c1=='z') rp = 19; // zM + else if (c1=='c') rp = 20; // cM } } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable c1 = variable_name[0]; @@ -22384,8 +23248,8 @@ namespace cimg_library_suffixed { c3 = variable_name[2]; c4 = variable_name[3]; if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds - } else if (!std::strcmp(variable_name,"interpolation")) rp = 30; // interpolation - else if (!std::strcmp(variable_name,"boundary")) rp = 31; // boundary + } else if (!std::strcmp(variable_name,"interpolation")) rp = 31; // interpolation + else if (!std::strcmp(variable_name,"boundary")) rp = 32; // boundary if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels @@ -22394,36 +23258,10 @@ namespace cimg_library_suffixed { if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; } } - // Tell for each character of an expression if it is inside a string or not. - CImg is_inside_string(CImg& _expr) const { - bool is_escaped = false, next_is_escaped = false; - unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string - CImg res = CImg::string(_expr); - bool *pd = res._data; - for (const char *ps = _expr._data; *ps; ++ps) { - if (!next_is_escaped && *ps=='\\') next_is_escaped = true; - if (!is_escaped && *ps=='\'') { // Non-escaped character - if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string - else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string - else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string - } - *(pd++) = mode>=1 || is_escaped; - mode = next_mode; - is_escaped = next_is_escaped; - next_is_escaped = false; - } - return res; - } - - // Return true if specified argument can be a part of an allowed variable name. - bool is_varchar(const char c) const { - return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; - } - // Return true if all values of a vector are computation values. bool is_comp_vector(const unsigned int arg) const { unsigned int siz = _cimg_mp_size(arg); - if (siz>8) return false; + if (siz>128) return false; const int *ptr = memtype.data(arg + 1); bool is_tmp = true; while (siz-->0) if (*(ptr++)) { is_tmp = false; break; } @@ -22433,36 +23271,44 @@ namespace cimg_library_suffixed { // Check if a memory slot is a positive integer constant scalar value. // 'mode' can be: // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant } - void check_constant(const unsigned int arg, const unsigned int n_arg, - const unsigned int mode, - char *const ss, char *const se, const char saved_char) { + void check_const_scalar(const unsigned int arg, const unsigned int n_arg, + const unsigned int mode, + char *const ss, char *const se, const char saved_char) { _cimg_mp_check_type(arg,n_arg,1,0); - if (!(_cimg_mp_is_constant(arg) && - (!mode || (double)(int)mem[arg]==mem[arg]) && + if (!_cimg_mp_is_const_scalar(arg)) { + const char *const s_arg = s_argth(n_arg); + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a constant, " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,s0); + } + const double val = mem[arg]; + + if (!((!mode || (double)(int)mem[arg]==mem[arg]) && (mode<2 || mem[arg]>=(mode==3)))) { const char *const s_arg = s_argth(n_arg); char *s0; _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, " - "in expression '%s%s%s'.", + "CImg<%s>::%s: %s%s %s%s (of type '%s' and value %g) is not a%s constant, " + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - s_arg,*s_arg?" argument":" Argument",s_type(arg)._data, + s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,val, !mode?"":mode==1?"n integer": - mode==2?" positive integer":" strictly positive integer", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + mode==2?" positive integer":" strictly positive integer",s0); } } // Check if an image index is a constant value. - void check_constant_index(const unsigned int arg, - char *const ss, char *const se, const char saved_char) { - if (arg!=~0U && !_cimg_mp_is_constant(arg)) { + void check_const_index(const unsigned int arg, + char *const ss, char *const se, const char saved_char) { + if (arg!=~0U && !_cimg_mp_is_const_scalar(arg)) { char *s0; _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s Specified image index is not a constant, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0); } } @@ -22480,11 +23326,10 @@ namespace cimg_library_suffixed { char *s0; _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s %s%s (of type '%s') " - "cannot be considered as a square matrix, in expression '%s%s%s'.", + "cannot be considered as a square matrix, in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), - s_type(arg)._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg)._data,s0); } } @@ -22517,29 +23362,32 @@ namespace cimg_library_suffixed { char *s0; _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), " - "in expression '%s%s%s'.", + "in expression '%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), - s_type(arg)._data,sb_type._data, - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s_type(arg)._data,sb_type._data,s0); } } - // Check that listin or listout are not empty. - void check_list(const bool is_out, - char *const ss, char *const se, const char saved_char) { - if ((!is_out && !listin) || (is_out && !listout)) { + // Check that imglist are not empty. + void check_list(char *const ss, char *const se, const char saved_char) { + if (!imglist) { char *s0; _cimg_mp_strerr; throw CImgArgumentException("[" cimg_appname "_math_parser] " - "CImg<%s>::%s: %s%s Invalid call with an empty image list, " - "in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", - s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + "CImg<%s>::%s: %s%s Image list cannot be empty, for expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0); } } + static void mp_check_list(_cimg_math_parser& mp, const char *const funcname) { + if (!mp.imglist) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>: Function '%s()': Images list cannot be empty.", + pixel_type(),funcname); + } + // Insert constant value in memory. - unsigned int constant(const double val) { + unsigned int const_scalar(const double val) { // Search for built-in constant. if (cimg::type::is_nan(val)) return _cimg_mp_slot_nan; @@ -22637,8 +23485,8 @@ namespace cimg_library_suffixed { return pos; } - // Set variable status to all values of a vector. - void set_variable_vector(const unsigned int arg) { + // Set reserved status to all values of a vector. + void set_reserved_vector(const unsigned int arg) { unsigned int siz = _cimg_mp_size(arg); int *ptr = memtype.data(arg + 1); while (siz-->0) *(ptr++) = -1; @@ -22647,12 +23495,14 @@ namespace cimg_library_suffixed { unsigned int scalar0(const mp_func op) { const unsigned int pos = scalar(); CImg::vector((ulongT)op,pos).move_to(code); + return_new_comp = true; return pos; } unsigned int scalar1(const mp_func op, const unsigned int arg1) { const unsigned int pos = - arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:scalar(); + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1: + ((return_new_comp = true), scalar()); CImg::vector((ulongT)op,pos,arg1).move_to(code); return pos; } @@ -22660,7 +23510,8 @@ namespace cimg_library_suffixed { unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { const unsigned int pos = arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: - arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:scalar(); + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + ((return_new_comp = true), scalar()); CImg::vector((ulongT)op,pos,arg1,arg2).move_to(code); return pos; } @@ -22670,7 +23521,8 @@ namespace cimg_library_suffixed { const unsigned int pos = arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: - arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:scalar(); + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + ((return_new_comp = true), scalar()); CImg::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code); return pos; } @@ -22682,7 +23534,8 @@ namespace cimg_library_suffixed { arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: - arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:scalar(); + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + ((return_new_comp = true), scalar()); CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code); return pos; } @@ -22695,7 +23548,8 @@ namespace cimg_library_suffixed { arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: - arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:scalar(); + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + ((return_new_comp = true), scalar()); CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code); return pos; } @@ -22709,7 +23563,8 @@ namespace cimg_library_suffixed { arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: - arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:scalar(); + arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + ((return_new_comp = true), scalar()); CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); return pos; } @@ -22725,7 +23580,8 @@ namespace cimg_library_suffixed { arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: - arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:scalar(); + arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7: + ((return_new_comp = true), scalar()); CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); return pos; } @@ -22753,8 +23609,9 @@ namespace cimg_library_suffixed { unsigned int vector1_v(const mp_func op, const unsigned int arg1) { const unsigned int siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code); + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,1,siz,(ulongT)op,arg1).move_to(code); else { code.insert(siz); for (unsigned int k = 1; k<=siz; ++k) @@ -22766,8 +23623,9 @@ namespace cimg_library_suffixed { unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { const unsigned int siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz); - if (siz>24) CImg::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vv,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code); else { code.insert(siz); for (unsigned int k = 1; k<=siz; ++k) @@ -22779,8 +23637,9 @@ namespace cimg_library_suffixed { unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { const unsigned int siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code); else { code.insert(siz); for (unsigned int k = 1; k<=siz; ++k) @@ -22792,8 +23651,9 @@ namespace cimg_library_suffixed { unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { const unsigned int siz = _cimg_mp_size(arg2), - pos = is_comp_vector(arg2)?arg2:vector(siz); - if (siz>24) CImg::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + pos = is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_sv,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code); else { code.insert(siz); for (unsigned int k = 1; k<=siz; ++k) @@ -22806,8 +23666,9 @@ namespace cimg_library_suffixed { const unsigned int arg3) { const unsigned int siz = _cimg_mp_size(arg1), - pos = is_comp_vector(arg1)?arg1:vector(siz); - if (siz>24) CImg::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code); + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,3,siz,(ulongT)op,arg1,arg2,arg3).move_to(code); else { code.insert(siz); for (unsigned int k = 1; k<=siz; ++k) @@ -22816,6 +23677,57 @@ namespace cimg_library_suffixed { return pos; } + unsigned int vector4_vvss(const mp_func op, const unsigned int arg1, const unsigned int arg2, + const unsigned int arg3, const unsigned int arg4) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) + CImg::vector((ulongT)mp_vector_map_vv,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2 + k,arg3,arg4). + move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector4_vsss(const mp_func op, const unsigned int arg1, const unsigned int arg2, + const unsigned int arg3, const unsigned int arg4) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) + CImg::vector((ulongT)mp_vector_map_v,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3,arg4). + move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector4_svss(const mp_func op, const unsigned int arg1, const unsigned int arg2, + const unsigned int arg3, const unsigned int arg4) { + const unsigned int + siz = _cimg_mp_size(arg2), + pos = is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) + CImg::vector((ulongT)mp_vector_map_sv,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1,arg2 + k,arg3,arg4). + move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + // Evaluation functions, known by the parser. // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT), // so we can store pointers to them directly in the opcode vectors. @@ -23203,6 +24115,10 @@ namespace cimg_library_suffixed { return _mp_correlate(mp,true); } + static double mp_copy(_cimg_math_parser& mp) { + return _mp_arg(2); + } + static double mp_correlate(_cimg_math_parser &mp) { return _mp_correlate(mp,false); } @@ -23220,18 +24136,20 @@ namespace cimg_library_suffixed { dM = (unsigned int)mp.opcode[10], sM = (unsigned int)mp.opcode[11], boundary_conditions = (unsigned int)_mp_arg(12), - channel_mode = (unsigned int)mp.opcode[14], - xcenter = (unsigned int)_mp_arg(15), - ycenter = (unsigned int)_mp_arg(16), - zcenter = (unsigned int)_mp_arg(17), - xstart = (unsigned int)mp.opcode[18], - ystart = (unsigned int)mp.opcode[19], - zstart = (unsigned int)mp.opcode[20], - xend = (unsigned int)mp.opcode[21], - yend = (unsigned int)mp.opcode[22], - zend = (unsigned int)mp.opcode[23]; + channel_mode = (unsigned int)mp.opcode[14]; const bool - is_normalized = (bool)_mp_arg(13); + is_normalized = (bool)_mp_arg(13), + interpolation_type = (bool)_mp_arg(30); + const int + xcenter = mp.opcode[15]!=~0U?(int)_mp_arg(15):(int)(~0U>>1), + ycenter = mp.opcode[16]!=~0U?(int)_mp_arg(16):(int)(~0U>>1), + zcenter = mp.opcode[17]!=~0U?(int)_mp_arg(17):(int)(~0U>>1), + xstart = (int)mp.opcode[18], + ystart = (int)mp.opcode[19], + zstart = (int)mp.opcode[20], + xend = (int)mp.opcode[21], + yend = (int)mp.opcode[22], + zend = (int)mp.opcode[23]; const float xstride = (float)_mp_arg(24), ystride = (float)_mp_arg(25), @@ -23247,7 +24165,8 @@ namespace cimg_library_suffixed { xstart,ystart,zstart, xend,yend,zend, xstride,ystride,zstride, - xdilation,ydilation,zdilation); + xdilation,ydilation,zdilation, + interpolation_type); else res = CImg(ptrA,wA,hA,dA,sA,true). get_correlate(CImg(ptrM,wM,hM,dM,sM,true), boundary_conditions,is_normalized,channel_mode, @@ -23255,7 +24174,8 @@ namespace cimg_library_suffixed { xstart,ystart,zstart, xend,yend,zend, xstride,ystride,zstride, - xdilation,ydilation,zdilation); + xdilation,ydilation,zdilation, + interpolation_type); CImg(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res; return cimg::type::nan(); } @@ -23268,6 +24188,23 @@ namespace cimg_library_suffixed { return std::cosh(_mp_arg(2)); } + static double mp_cov(_cimg_math_parser& mp) { + const unsigned int + _siz = (unsigned int)mp.opcode[4], + siz = std::max(_siz,1U), + off = _siz?1:0, + sizm1 = siz>1?siz - 1:1; + const CImg + A(&_mp_arg(2) + off,1,siz,1,1,true), + B(&_mp_arg(3) + off,1,siz,1,1,true); + const double + avgA = (unsigned int)mp.opcode[5]==~0U?A.mean():_mp_arg(5), + avgB = (unsigned int)mp.opcode[6]==~0U?B.mean():_mp_arg(6); + double res = 0; + cimg_forY(A,k) res+=(A[k] - avgA)*(B[k] - avgB); + return res/sizm1; + } + static double mp_critical(_cimg_math_parser& mp) { const ulongT g_target = mp.opcode[1]; cimg_pragma_openmp(critical(mp_critical)) @@ -23283,7 +24220,7 @@ namespace cimg_library_suffixed { return mp.mem[g_target]; } - static double mp_crop(_cimg_math_parser& mp) { + static double mp_image_crop(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); const unsigned int @@ -23293,8 +24230,8 @@ namespace cimg_library_suffixed { dc = (unsigned int)mp.opcode[10]; const unsigned int boundary_conditions = (unsigned int)_mp_arg(11); unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + const CImg &img = ind==~0U?mp.imgin:mp.imglist[ind]; if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, x + dx - 1,y + dy - 1, @@ -23317,6 +24254,149 @@ namespace cimg_library_suffixed { return valcmax?cmax:val; } + static double mp_da_back_or_pop(_cimg_math_parser& mp) { + const bool is_pop = (bool)mp.opcode[4]; + const char *const s_op = is_pop?"da_pop":"da_back"; + mp_check_list(mp,s_op); + const unsigned int + dim = (unsigned int)mp.opcode[2], + ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); + double *const ptrd = &_mp_arg(1) + (dim>1?1:0); + CImg &img = mp.imglist[ind]; + int siz = img?(int)img[img._height - 1]:0; + if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': " + "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", + mp.imgout.pixel_type(),s_op,ind, + img.width(),img.height(),img.depth(),img.spectrum(), + img._width==1 && img._depth==1?"":" (contains invalid element counter)"); + if (!siz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': " + "Specified dynamic array #%u contains no elements.", + mp.imgout.pixel_type(),s_op,ind); + + double ret = cimg::type::nan(); + if (dim<1) ret = img[siz - 1]; // Scalar element + else cimg_forC(img,c) ptrd[c] = img(0,siz - 1,0,c); // Vector element + if (is_pop) { // Remove element from array + --siz; + if (img.height()>32 && siz<2*img.height()/3) // Reduce size of dynamic array + img.resize(1,std::max(2*siz + 1,32),1,-100,0); + img[img._height - 1] = (T)siz; + } + return ret; + } + + static double mp_da_freeze(_cimg_math_parser& mp) { + const char *const s_op = "da_freeze"; + mp_check_list(mp,s_op); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; + int siz = img?(int)img[img._height - 1]:0; + if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': " + "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", + mp.imgout.pixel_type(),s_op,ind, + img.width(),img.height(),img.depth(),img.spectrum(), + img._width==1 && img._depth==1?"":" (contains invalid element counter)"); + if (siz) img.resize(1,siz,1,-100,0,0); else img.assign(); + return cimg::type::nan(); + } + + static double mp_da_insert_or_push(_cimg_math_parser& mp) { + const char *const s_op = mp.opcode[3]==~0U?"da_push":"da_insert"; + mp_check_list(mp,s_op); + const unsigned int + dim = (unsigned int)mp.opcode[4], + _dim = std::max(1U,dim), + nb_elts = (unsigned int)mp.opcode[5] - 6, + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; + const int + siz = img?(int)img[img._height - 1]:0, + pos0 = mp.opcode[3]==~0U?siz:(int)_mp_arg(3), + pos = pos0<0?pos0 + siz:pos0; + + if (img && _dim!=img._spectrum) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': " + "Element to insert has invalid size %u (should be %u).", + mp.imgout.pixel_type(),s_op,_dim,img._spectrum); + if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': " + "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", + mp.imgout.pixel_type(),s_op,ind, + img.width(),img.height(),img.depth(),img.spectrum(), + img._width==1 && img._depth==1?"":" (contains invalid element counter)"); + if (pos<0 || pos>siz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': " + "Invalid position %d (not in range -%d...%d).", + mp.imgout.pixel_type(),s_op,pos0,siz,siz); + + if (siz + nb_elts + 1>=img._height) // Increase size of dynamic array, if necessary + img.resize(1,2*siz + nb_elts + 1,1,_dim,0); + + if (pos!=siz) // Move existing data in dynamic array + cimg_forC(img,c) std::memmove(img.data(0,pos + nb_elts,0,c),img.data(0,pos,0,c),(siz - pos)*sizeof(T)); + + if (!dim) // Scalar or vector1() elements + for (unsigned int k = 0; k1 + for (unsigned int k = 0; k::nan(); + } + + static double mp_da_remove(_cimg_math_parser& mp) { + mp_check_list(mp,"da_remove"); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; + int siz = img?(int)img[img._height - 1]:0; + if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': " + "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", + mp.imgout.pixel_type(),ind, + img.width(),img.height(),img.depth(),img.spectrum(), + img._width==1 && img._depth==1?"":" (contains invalid element counter)"); + if (!siz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': " + "Dynamic array is empty.", + mp.imgout.pixel_type()); + int + start0 = mp.opcode[3]==~0U?siz - 1:_mp_arg(3), + end0 = mp.opcode[4]==~0U?start0:_mp_arg(4), + start = start0<0?start0 + siz:start0, + end = end0<0?end0 + siz:end0; + if (start<0 || start>=siz || end<0 || end>=siz || start>end) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': " + "Invalid starting (%d) and ending (%d) positions " + "(not ordered, in range -%d...%d).", + mp.imgout.pixel_type(),start0,end0,siz,siz - 1); + if (end32 && siz<2*img.height()/3) // Reduce size of dynamic array + img.resize(1,std::max(2*siz + 1,32),1,-100,0); + img[img._height - 1] = (T)siz; + return cimg::type::nan(); + } + + static double mp_da_size(_cimg_math_parser& mp) { + mp_check_list(mp,"da_size"); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; + const int siz = img?(int)img[img._height - 1]:0; + if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_size()': " + "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", + mp.imgout.pixel_type(),ind, + img.width(),img.height(),img.depth(),img.spectrum(), + img._width==1 && img._depth==1?"":" (contains invalid element counter)"); + return siz; + } + static double mp_date(_cimg_math_parser& mp) { const unsigned int siz_out = (unsigned int)mp.opcode[2], @@ -23363,7 +24443,7 @@ namespace cimg_library_suffixed { { std::fprintf(cimg::output(), "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" - "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", + "Start debugging '%s', code length: %u -> mem[%u] (memsize: %u)", (void*)&mp,n_thread,mp.debug_indent,' ', expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width); std::fflush(cimg::output()); @@ -23398,7 +24478,7 @@ namespace cimg_library_suffixed { mp.debug_indent-=3; std::fprintf(cimg::output(), "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" - "End debugging expression '%s' -> mem[%u] = %.17g (memsize: %u)", + "End debugging '%s' -> mem[%u] = %.17g (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()); @@ -23411,6 +24491,10 @@ namespace cimg_library_suffixed { return _mp_arg(2) - 1; } + static double mp_deg2rad(_cimg_math_parser& mp) { + return _mp_arg(2)*cimg::PI/180; + } + static double mp_det(_cimg_math_parser& mp) { const double *ptrs = &_mp_arg(2) + 1; const unsigned int k = (unsigned int)mp.opcode[3]; @@ -23506,47 +24590,9 @@ namespace cimg_library_suffixed { return mp.mem[mem_body]; } - static double mp_draw(_cimg_math_parser& mp) { - const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); - unsigned int ind = (unsigned int)mp.opcode[3]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); - CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; - unsigned int - dx = (unsigned int)mp.opcode[8], - dy = (unsigned int)mp.opcode[9], - dz = (unsigned int)mp.opcode[10], - dc = (unsigned int)mp.opcode[11]; - dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); - dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); - dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); - dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); - - const ulongT sizS = mp.opcode[2]; - if (sizS<(ulongT)dx*dy*dz*dc) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " - "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " - "(%lu values) do not match.", - mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); - CImg S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); - const float opacity = (float)_mp_arg(12); - - if (img._data) { - if (mp.opcode[13]!=~0U) { // Opacity mask specified - const ulongT sizM = mp.opcode[14]; - if (sizM<(ulongT)dx*dy*dz) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " - "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " - "(%lu values) do not match.", - mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); - const CImg M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); - img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); - } else img.draw_image(x,y,z,c,S,opacity); - } - return cimg::type::nan(); - } - static double mp_echo(_cimg_math_parser& mp) { const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type::nan(); } // No arguments CImgList _str; CImg it; for (unsigned int n = 0; n &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); + } + CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; CImg color(img._spectrum,1,1,1,0); bool is_invalid_arguments = false, is_outlined = false; float r1 = 0, r2 = 0, angle = 0, opacity = 1; @@ -23591,7 +24641,7 @@ namespace cimg_library_suffixed { else { r2 = (float)_mp_arg(i++); if (i ss(sizs + 1); cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i]; ss.back() = 0; - if (!sizd) return CImg(w,h,d,s,0).eval(ss,0,0,0,0,&mp.listin,&mp.listout); // Scalar result - CImg(++ptrd,w,h,d,s,true) = CImg(w,h,d,s,0).fill(ss,true,true,&mp.listin,&mp.listout); + if (!sizd) return CImg(w,h,d,s,0).eval(ss,0,0,0,0,&mp.imglist); // Scalar result + CImg(++ptrd,w,h,d,s,true) = CImg(w,h,d,s,0).fill(ss,true,true,&mp.imglist); return cimg::type::nan(); } @@ -23673,6 +24731,50 @@ namespace cimg_library_suffixed { return cimg::fibonacci((int)_mp_arg(2)); } + static double mp_fill(_cimg_math_parser& mp) { + unsigned int siz = (unsigned int)mp.opcode[2]; + double + *ptrd = &_mp_arg(1), + *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0, + *const ptrs = &_mp_arg(4); + if (siz) ++ptrd; else ++siz; // Fill vector-valued slot + const CImg + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[5]; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + unsigned int it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + *ptrc = (double)it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return *ptrd; + } + static double mp_find(_cimg_math_parser& mp) { const int _step = (int)_mp_arg(6), step = _step?_step:-1; const ulongT siz = (ulongT)mp.opcode[3]; @@ -23842,7 +24944,7 @@ namespace cimg_library_suffixed { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind==~0U) std::memset(ptr,0,siz*sizeof(double)); else { - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); cimg_mp_func_name(ind,ptr,siz); } return cimg::type::nan(); @@ -23889,15 +24991,19 @@ namespace cimg_library_suffixed { static double mp_image_d(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.depth(); } static double mp_image_display(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + mp_check_list(mp,"display"); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); cimg::mutex(6); - CImg &img = mp.listout[ind]; + CImg &img = mp.imglist[ind]; CImg title(256); std::fputc('\n',cimg::output()); cimg_snprintf(title,title._width,"[ Image #%u ]",ind); @@ -23906,31 +25012,83 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } + static double mp_image_draw(_cimg_math_parser& mp) { + const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); + } + CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; + unsigned int + dx = (unsigned int)mp.opcode[8], + dy = (unsigned int)mp.opcode[9], + dz = (unsigned int)mp.opcode[10], + dc = (unsigned int)mp.opcode[11]; + dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); + dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); + dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); + dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); + + const ulongT sizS = mp.opcode[2]; + if (sizS<(ulongT)dx*dy*dz*dc) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Sprite vector (%lu values) and its specified geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + const CImg S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); + const float opacity = (float)_mp_arg(12); + + if (img._data) { + if (mp.opcode[13]!=~0U) { // Opacity mask specified + const ulongT sizM = mp.opcode[14]; + if (sizM<(ulongT)dx*dy*dz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Mask vector (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + const CImg M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); + img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); + } else img.draw_image(x,y,z,c,S,opacity); + } + return cimg::type::nan(); + } + static double mp_image_h(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.height(); } static double mp_image_median(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.median(); } static double mp_image_norm(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.magnitude(); } static double mp_image_print(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + mp_check_list(mp,"print"); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); cimg::mutex(6); - CImg &img = mp.listout[ind]; + CImg &img = mp.imglist[ind]; CImg title(256); std::fputc('\n',cimg::output()); cimg_snprintf(title,title._width,"[ Image #%u ]",ind); @@ -23940,9 +25098,10 @@ namespace cimg_library_suffixed { } static double mp_image_resize(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + mp_check_list(mp,"resize"); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); cimg::mutex(6); - CImg &img = mp.listout[ind]; + CImg &img = mp.imglist[ind]; const double _w = mp.opcode[3]==~0U?-100:_mp_arg(3), _h = mp.opcode[4]==~0U?-100:_mp_arg(4), @@ -23975,18 +25134,22 @@ namespace cimg_library_suffixed { static double mp_image_s(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.spectrum(); } static double mp_image_sort(_cimg_math_parser& mp) { + mp_check_list(mp,"sort"); const bool is_increasing = (bool)_mp_arg(3); const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), axis = (unsigned int)_mp_arg(4); cimg::mutex(6); - CImg &img = mp.listout[ind]; + CImg &img = mp.imglist[ind]; img.sort(is_increasing, axis==0 || axis=='x'?'x': axis==1 || axis=='y'?'y': @@ -23999,39 +25162,53 @@ namespace cimg_library_suffixed { static double mp_image_stats(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind==~0U) CImg(ptrd,14,1,1,1,true) = mp.imgout.get_stats(); + if (ind==~0U) + CImg(ptrd,14,1,1,1,true) = mp.imgout.get_stats(); else { - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats(); + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg(ptrd,14,1,1,1,true) = mp.imglist[ind].get_stats(); } return cimg::type::nan(); } static double mp_image_w(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.width(); } static double mp_image_wh(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.width()*img.height(); } static double mp_image_whd(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.width()*img.height()*img.depth(); } static double mp_image_whds(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + } + const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; return (double)img.width()*img.height()*img.depth()*img.spectrum(); } @@ -24111,8 +25288,9 @@ namespace cimg_library_suffixed { } static double mp_isdir(_cimg_math_parser& mp) { - const double *ptrs = &_mp_arg(2) + 1; - const ulongT siz = (ulongT)mp.opcode[3]; + const unsigned int siz = (unsigned int)mp.opcode[3]; + const double *const ptrs = &_mp_arg(2) + (siz?1:0); + if (!siz) { char str[2] = {}; *str = *ptrs; return (double)cimg::is_directory(str); } CImg ss(siz + 1); cimg_forX(ss,i) ss[i] = (char)ptrs[i]; ss.back() = 0; @@ -24120,11 +25298,24 @@ namespace cimg_library_suffixed { } static double mp_isin(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - const double val = _mp_arg(3); - for (unsigned int i = 4; i ref(&_mp_arg(3) + 1,siz_ref,1,1,1,true); + for (unsigned int i = 5; i(&_mp_arg(i) + 1,siz,1,1,1,true)==ref) { res = true; break; } + } + } else { // Reference value is a scalar + const double ref = _mp_arg(3); + for (unsigned i = 5; i ss(siz + 1); cimg_forX(ss,i) ss[i] = (char)ptrs[i]; ss.back() = 0; @@ -24148,6 +25340,18 @@ namespace cimg_library_suffixed { return (double)cimg::type::is_nan(_mp_arg(2)); } + static double mp_isvarname(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[3]; + const double *ptrs = &_mp_arg(2) + (siz?1:0); + if (!siz) { + const char c = (char)*ptrs; + return (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_'; + } + if (*ptrs>='0' && *ptrs<='9') return 0; + for (unsigned int k = 0; k &img = mp.listin[indi]; + indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + const CImg &img = mp.imglist[indi]; const int _step = (int)_mp_arg(5), step = _step?_step:-1; const ulongT siz = (ulongT)img.size(); longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1); @@ -24392,9 +25596,9 @@ namespace cimg_library_suffixed { static double mp_list_find_seq(_cimg_math_parser& mp) { const unsigned int - indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = mp.listin[indi]; - const int _step = (bool)_mp_arg(6), step = _step?_step:-1; + indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + const CImg &img = mp.imglist[indi]; + const int _step = (int)_mp_arg(6), step = _step?_step:-1; const ulongT siz1 = (ulongT)img.size(), siz2 = (ulongT)mp.opcode[4]; @@ -24434,15 +25638,15 @@ namespace cimg_library_suffixed { } static double mp_list_height(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._height; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + return (double)mp.imglist[ind]._height; } static double mp_list_ioff(_cimg_math_parser& mp) { const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), boundary_conditions = (unsigned int)_mp_arg(4); - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const longT off = (longT)_mp_arg(3), whds = (longT)img.size(); @@ -24463,16 +25667,16 @@ namespace cimg_library_suffixed { } 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; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + return (double)mp.imglist[ind]._is_shared; } static double mp_list_ixyzc(_cimg_math_parser& mp) { const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), interpolation = (unsigned int)_mp_arg(7), boundary_conditions = (unsigned int)_mp_arg(8); - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5), c = _mp_arg(6); @@ -24548,12 +25752,12 @@ namespace cimg_library_suffixed { static double mp_list_joff(_cimg_math_parser& mp) { const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), boundary_conditions = (unsigned int)_mp_arg(4); const int ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const longT off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), whds = (longT)img.size(); @@ -24575,10 +25779,10 @@ namespace cimg_library_suffixed { static double mp_list_jxyzc(_cimg_math_parser& mp) { const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), interpolation = (unsigned int)_mp_arg(7), boundary_conditions = (unsigned int)_mp_arg(8); - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], @@ -24655,26 +25859,27 @@ namespace cimg_library_suffixed { } static double mp_list_l(_cimg_math_parser& mp) { - return (double)mp.listout.width(); + return (double)mp.imglist.width(); } static double mp_list_median(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - if (!mp.list_median) mp.list_median.assign(mp.listin._width); - if (!mp.list_median[ind]) CImg::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + if (!mp.list_median) mp.list_median.assign(mp.imglist._width); + if (!mp.list_median[ind]) CImg::vector(mp.imglist[ind].median()).move_to(mp.list_median[ind]); return *mp.list_median[ind]; } static double mp_list_norm(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - if (!mp.list_norm) mp.list_norm.assign(mp.listin._width); - if (!mp.list_norm[ind]) CImg::vector(mp.listin[ind].magnitude()).move_to(mp.list_norm[ind]); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + if (!mp.list_norm) mp.list_norm.assign(mp.imglist._width); + if (!mp.list_norm[ind]) CImg::vector(mp.imglist[ind].magnitude()).move_to(mp.list_norm[ind]); return *mp.list_norm[ind]; } static double mp_list_set_ioff(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const longT off = (longT)_mp_arg(3), whds = (longT)img.size(); @@ -24684,8 +25889,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_ixyzc(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); @@ -24697,8 +25903,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_joff(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const int ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; @@ -24711,8 +25918,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_jxyzc(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; @@ -24727,8 +25935,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Ioff_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const longT off = (longT)_mp_arg(3), whd = (longT)img.width()*img.height()*img.depth(); @@ -24741,8 +25950,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Ioff_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const longT off = (longT)_mp_arg(3), whd = (longT)img.width()*img.height()*img.depth(); @@ -24756,8 +25966,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), @@ -24772,8 +25983,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), @@ -24789,8 +26001,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Joff_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const int ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; @@ -24806,8 +26019,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Joff_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const int ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; @@ -24824,8 +26038,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; const int x = (int)(ox + _mp_arg(3)), @@ -24841,8 +26056,9 @@ namespace cimg_library_suffixed { } static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - CImg &img = mp.listout[ind]; + if (!mp.imglist.width()) return cimg::type::nan(); + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + CImg &img = mp.imglist[ind]; const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; const int x = (int)(ox + _mp_arg(3)), @@ -24859,46 +26075,56 @@ namespace cimg_library_suffixed { } static double mp_list_spectrum(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._spectrum; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + return (double)mp.imglist[ind]._spectrum; } static double mp_list_stats(_cimg_math_parser& mp) { const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), k = (unsigned int)mp.opcode[3]; - if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); - if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); + bool get_stats = false; + cimg::mutex(13); + if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width); + if (!mp.list_stats[ind]) get_stats = true; + cimg::mutex(13,0); + + if (get_stats) { + CImg st = mp.imglist[ind].get_stats(); + cimg::mutex(13); + st.move_to(mp.list_stats[ind]); + cimg::mutex(13,0); + } return mp.list_stats(ind,k); } static double mp_list_wh(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + return (double)mp.imglist[ind]._width*mp.imglist[ind]._height; } static double mp_list_whd(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + return (double)mp.imglist[ind]._width*mp.imglist[ind]._height*mp.imglist[ind]._depth; } static double mp_list_whds(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + return (double)mp.imglist[ind]._width*mp.imglist[ind]._height*mp.imglist[ind]._depth*mp.imglist[ind]._spectrum; } static double mp_list_width(_cimg_math_parser& mp) { - const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - return (double)mp.listin[ind]._width; + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + return (double)mp.imglist[ind]._width; } static double mp_list_Ioff(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), boundary_conditions = (unsigned int)_mp_arg(4), vsiz = (unsigned int)mp.opcode[5]; - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const longT off = (longT)_mp_arg(3), whd = (longT)img.width()*img.height()*img.depth(); @@ -24934,11 +26160,11 @@ namespace cimg_library_suffixed { static double mp_list_Ixyz(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), interpolation = (unsigned int)_mp_arg(6), boundary_conditions = (unsigned int)_mp_arg(7), vsiz = (unsigned int)mp.opcode[8]; - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5); const ulongT whd = (ulongT)img._width*img._height*img._depth; const T *ptrs; @@ -25020,12 +26246,12 @@ namespace cimg_library_suffixed { static double mp_list_Joff(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), boundary_conditions = (unsigned int)_mp_arg(4), vsiz = (unsigned int)mp.opcode[5]; const int ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z]; - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const longT off = img.offset(ox,oy,oz) + (longT)_mp_arg(3), whd = (longT)img.width()*img.height()*img.depth(); @@ -25061,11 +26287,11 @@ namespace cimg_library_suffixed { static double mp_list_Jxyz(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const unsigned int - ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()), interpolation = (unsigned int)_mp_arg(6), boundary_conditions = (unsigned int)_mp_arg(7), vsiz = (unsigned int)mp.opcode[8]; - const CImg &img = mp.listin[ind]; + const CImg &img = mp.imglist[ind]; const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5); @@ -25202,6 +26428,23 @@ namespace cimg_library_suffixed { return (double)(_mp_arg(2)<=_mp_arg(3)); } + static double mp_map(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptrX = &_mp_arg(2) + 1, + *ptrP = &_mp_arg(3) + 1; + const unsigned int + sizX = (unsigned int)mp.opcode[4], + sizP = (unsigned int)mp.opcode[5], + nb_channelsX = (unsigned int)mp.opcode[6], + nb_channelsP = (unsigned int)mp.opcode[7], + boundary_conditions = (unsigned int)_mp_arg(8); + CImg(ptrd,sizX/nb_channelsX,1,1,nb_channelsX*nb_channelsP,true) = + CImg(ptrX,sizX/nb_channelsX,1,1,nb_channelsX,true). + get_map(CImg(ptrP,sizP/nb_channelsP,1,1,nb_channelsP,true),boundary_conditions); + return cimg::type::nan(); + } + static double mp_matrix_eig(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const double *ptr1 = &_mp_arg(2) + 1; @@ -25216,9 +26459,12 @@ namespace cimg_library_suffixed { static double mp_matrix_invert(_cimg_math_parser& mp) { double *const ptrd = &_mp_arg(1) + 1; const double *const ptr1 = &_mp_arg(2) + 1; - const unsigned int k = (unsigned int)mp.opcode[3]; - const bool use_LU = (bool)_mp_arg(4); - CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(use_LU); + const unsigned int + w = (unsigned int)mp.opcode[3], + h = (unsigned int)mp.opcode[4]; + const bool use_LU = (bool)_mp_arg(5); + const float lambda = (float)_mp_arg(6); + CImg(ptrd,h,w,1,1,true) = CImg(ptr1,w,h,1,1,true).get_invert(use_LU,lambda); return cimg::type::nan(); } @@ -25235,17 +26481,6 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } - static double mp_matrix_pseudoinvert(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const double *ptr1 = &_mp_arg(2) + 1; - const unsigned int - k = (unsigned int)mp.opcode[3], - l = (unsigned int)mp.opcode[4]; - const bool use_LU = (bool)_mp_arg(5); - CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_pseudoinvert(use_LU); - return cimg::type::nan(); - } - static double mp_matrix_svd(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const double *ptr1 = &_mp_arg(2) + 1; @@ -25295,8 +26530,8 @@ namespace cimg_library_suffixed { const longT siz, const long inc, const bool is_out) { const unsigned ind = (unsigned int)p_ref[1]; const CImg &img = is_out? - (ind==~0U?mp.imgout:mp.listout[cimg::mod((int)mp.mem[ind],mp.listout.width())]): - (ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]); + (ind==~0U?mp.imgout:mp.imglist[cimg::mod((int)mp.mem[ind],mp.imglist.width())]): + (ind==~0U?mp.imgin:mp.imglist[cimg::mod((int)mp.mem[ind],mp.imglist.width())]); const bool is_relative = (bool)p_ref[2]; int ox, oy, oz, oc; longT off = 0; @@ -25451,6 +26686,15 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } + static double mp_mse(_cimg_math_parser& mp) { + const unsigned int + _siz = (unsigned int)mp.opcode[4], + siz = std::max(_siz,1U), + off = _siz?1:0; + return CImg(&_mp_arg(2) + off,1,siz,1,1,true). + MSE(CImg(&_mp_arg(3) + off,1,siz,1,1,true)); + } + static double mp_mul(_cimg_math_parser& mp) { return _mp_arg(2)*_mp_arg(3); } @@ -25531,8 +26775,11 @@ namespace cimg_library_suffixed { static double mp_polygon(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; unsigned int ind = (unsigned int)mp.opcode[3]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); - CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + if (ind!=~0U) { + if (!mp.imglist.width()) return cimg::type::nan(); + ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); + } + CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; bool is_invalid_arguments = i_end<=4, is_outlined = false; if (!is_invalid_arguments) { int nbv = (int)_mp_arg(4); @@ -25620,8 +26867,51 @@ namespace cimg_library_suffixed { return val; } - static double mp_copy(_cimg_math_parser& mp) { - return _mp_arg(2); + static double mp_rad2deg(_cimg_math_parser& mp) { + return _mp_arg(2)*180/cimg::PI; + } + + static double mp_repeat(_cimg_math_parser& mp) { + const double nb_it = _mp_arg(2); + double + *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0, + *const ptrs = &_mp_arg(1); + const CImg + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[4]; + + if (nb_it>0) { + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + double it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + *ptrc = it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + mp.break_type = _break_type; + } + + mp.p_code = p_end - 1; + return *ptrs; } static double mp_rol(_cimg_math_parser& mp) { @@ -25635,7 +26925,7 @@ namespace cimg_library_suffixed { static double mp_rot2d(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const float - theta = (float)_mp_arg(2)*cimg::PI/180, + theta = (float)_mp_arg(2), ca = std::cos(theta), sa = std::sin(theta); *(ptrd++) = ca; @@ -25647,8 +26937,12 @@ namespace cimg_library_suffixed { static double mp_rot3d(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; - const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5); - CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); + const float + x = (float)_mp_arg(2), + y = (float)_mp_arg(3), + z = (float)_mp_arg(4), + theta = (float)_mp_arg(5); + CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta*180/cimg::PI); return cimg::type::nan(); } @@ -25738,6 +27032,22 @@ namespace cimg_library_suffixed { return _mp_arg(1)-=_mp_arg(2); } +#ifdef cimg_mp_func_set + static double mp_set(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(1); + double *ptrd = &_mp_arg(3) + 1; + const unsigned int + sizs = (unsigned int)mp.opcode[2], + sizd = (unsigned int)mp.opcode[4]; + CImg sd(sizd + 1); + cimg_for_inX(sd,0,sd.width() - 1,i) sd[i] = (char)ptrd[i]; + sd.back() = 0; + if (sizs) cimg_mp_func_set(ptrs + 1,sizs,sd._data); + else cimg_mp_func_set(ptrs,0,sd._data); + return *ptrs; + } +#endif + static double mp_set_ioff(_cimg_math_parser& mp) { CImg &img = mp.imgout; const longT @@ -25948,7 +27258,9 @@ namespace cimg_library_suffixed { k = (unsigned int)mp.opcode[4], l = (unsigned int)mp.opcode[5], m = (unsigned int)mp.opcode[6]; - CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,false).solve(CImg(ptr1,k,l,1,1,true)); + const bool use_LU = (bool)_mp_arg(7); + CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,false). + solve(CImg(ptr1,k,l,1,1,true),use_LU); return cimg::type::nan(); } @@ -26018,8 +27330,7 @@ namespace cimg_library_suffixed { *ptr2 = &_mp_arg(4) + 1; const unsigned int siz1 = (unsigned int)mp.opcode[3], - siz2 = (unsigned int)mp.opcode[5], - sizM = std::max(siz1,1U); + siz2 = (unsigned int)mp.opcode[5]; const int w = (int)_mp_arg(6), h = (int)_mp_arg(7), @@ -26031,22 +27342,19 @@ namespace cimg_library_suffixed { throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': " "Specified image dimensions (%d,%d,%d,%d) are invalid.", pixel_type(),w,h,d,s); - if ((unsigned int)w*h*d*s>sizM) - throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': " - "Specified image dimensions (%d,%d,%d,%d) are too large for vector size (%u).", - pixel_type(),w,h,d,s,sizM); CImg ss(siz2 + 1); cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i]; ss.back() = 0; - if (siz1) cimg_mp_func_store(ptr1 + 1, + if (siz1) cimg_mp_func_store(ptr1 + 1,siz1, (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, is_compressed,ss._data); - else cimg_mp_func_store(ptr1,1,1,1,1,is_compressed,ss._data); + else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, + is_compressed,ss._data); return cimg::type::nan(); } #endif - static double mp_stov(_cimg_math_parser& mp) { + static double mp_s2v(_cimg_math_parser& mp) { const double *ptrs = &_mp_arg(2); const ulongT siz = (ulongT)mp.opcode[3]; longT ind = (longT)_mp_arg(4); @@ -26163,6 +27471,20 @@ namespace cimg_library_suffixed { return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng); } + static double mp_u_ext(_cimg_math_parser& mp) { // Extended version with extremum control + const double eps = 1e-5; + const bool + include_min = (bool)_mp_arg(4), + include_max = (bool)_mp_arg(5); + double + m = _mp_arg(2), + M = _mp_arg(3); + if (m>M) cimg::swap(m,M); + if (!include_min) m = m>0?m*(1 + eps):m<0?m*(1 - eps):eps; + if (!include_max) M = M>0?M*(1 - eps):M<0?M*(1 + eps):-eps; + return cimg::rand(m,M,&mp.rng); + } + static double mp_ui2f(_cimg_math_parser& mp) { return (double)cimg::uint2float((unsigned int)_mp_arg(2)); } @@ -26203,6 +27525,77 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } + static double mp_vector_crop_ext(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int + w = (unsigned int)mp.opcode[3], + h = (unsigned int)mp.opcode[4], + d = (unsigned int)mp.opcode[5], + s = (unsigned int)mp.opcode[6], + dx = (unsigned int)mp.opcode[11], + dy = (unsigned int)mp.opcode[12], + dz = (unsigned int)mp.opcode[13], + dc = (unsigned int)mp.opcode[14], + boundary_conditions = (int)_mp_arg(15); + const int x = (int)_mp_arg(7), y = (int)_mp_arg(8), z = (int)_mp_arg(9), c = (int)_mp_arg(10); + CImg(ptrd,dx,dy,dz,dc,true) = CImg(ptrs,w,h,d,s,true). + get_crop(x,y,z,c,x + dx - 1,y + dy - 1,z + dz - 1,c + dc - 1,boundary_conditions); + return cimg::type::nan(); + } + + static double mp_vector_draw(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(7) + 1; + const unsigned int + sizD = (unsigned int)mp.opcode[2], + sizS = (unsigned int)mp.opcode[8]; + const int + w = (int)_mp_arg(3), h = (int)_mp_arg(4), d = (int)_mp_arg(5), s = (int)_mp_arg(6), + x = (int)_mp_arg(9), y = (int)_mp_arg(10), z = (int)_mp_arg(11), c = (int)_mp_arg(12); + int dx = (int)mp.opcode[13], dy = (int)mp.opcode[14], dz = (int)mp.opcode[15], dc = (int)mp.opcode[16]; + dx = (unsigned int)dx==~0U?w:(int)_mp_arg(13); + dy = (unsigned int)dy==~0U?h:(int)_mp_arg(14); + dz = (unsigned int)dz==~0U?d:(int)_mp_arg(15); + dc = (unsigned int)dc==~0U?s:(int)_mp_arg(16); + + if (w<=0 || h<=0 || d<=0 || s<=0) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Invalid specified target vector geometry (%d,%d,%d,%d).", + mp.imgin.pixel_type(),w,h,d,s); + if (sizD<(ulongT)w*h*d*s) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Target vector (%lu values) and its specified target geometry (%d,%d,%d,%d) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizD,w,h,d,s,(ulongT)w*h*d*s); + if (dx<=0 || dy<=0 || dz<=0 || dc<=0) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Invalid specified sprite geometry (%d,%d,%d,%d).", + mp.imgin.pixel_type(),dx,dy,dz,dc); + if (sizS<(ulongT)dx*dy*dz*dc) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Sprite vector (%lu values) and its specified sprite geometry (%d,%d,%d,%d) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + + CImg D(ptrd,w,h,d,s,true); + const CImg S(ptrs,dx,dy,dz,dc,true); + const float opacity = (float)_mp_arg(17); + + if (mp.opcode[18]!=~0U) { // Opacity mask specified + const ulongT sizM = mp.opcode[19]; + if (sizM<(ulongT)dx*dy*dz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Mask vector (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + const CImg M(&_mp_arg(18) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); + D.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(20)); + } else D.draw_image(x,y,z,c,S,opacity); + + return cimg::type::nan(); + } + static double mp_vector_init(_cimg_math_parser& mp) { unsigned int ptrs = 4U, @@ -26297,77 +27690,51 @@ namespace cimg_library_suffixed { return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type::nan(); } - static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector) + static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector,[...]) unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[5] + 1; + nb_args = (unsigned int)mp.opcode[2], + siz_vector = (unsigned int)mp.opcode[3], + ptrs = (unsigned int)mp.opcode[6] + 1; double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(4); - l_opcode[2] = mp.opcode[4]; // Scalar argument1 + mp_func op = (mp_func)mp.opcode[4]; + CImg l_opcode(mp.opcode._data + 3,nb_args + 2); + l_opcode[0] = mp.opcode[1]; l_opcode.swap(mp.opcode); ulongT &argument2 = mp.opcode[3]; - while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } + while (siz_vector-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } l_opcode.swap(mp.opcode); return cimg::type::nan(); } - static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector) + static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector,[...]) unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; + nb_args = (unsigned int)mp.opcode[2], + siz_vector = (unsigned int)mp.opcode[3], + ptrs = (unsigned int)mp.opcode[5] + 1; double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,3); + mp_func op = (mp_func)mp.opcode[4]; + CImg l_opcode(mp.opcode._data + 3,nb_args + 2); + l_opcode[0] = l_opcode[1]; l_opcode.swap(mp.opcode); ulongT &argument = mp.opcode[2]; - while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } + while (siz_vector-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } l_opcode.swap(mp.opcode); return cimg::type::nan(); } - static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar) + static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector,[...]) unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; + nb_args = (unsigned int)mp.opcode[2], + siz_vector = (unsigned int)mp.opcode[3], + ptrs1 = (unsigned int)mp.opcode[5] + 1, + ptrs2 = (unsigned int)mp.opcode[6] + 1; double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,4); - l_opcode[3] = mp.opcode[5]; // Scalar argument2 - l_opcode.swap(mp.opcode); - ulongT &argument1 = mp.opcode[2]; - while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs = (unsigned int)mp.opcode[4] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,5); - l_opcode[3] = mp.opcode[5]; // Scalar argument2 - l_opcode[4] = mp.opcode[6]; // Scalar argument3 - l_opcode.swap(mp.opcode); - ulongT &argument1 = mp.opcode[2]; - while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } - l_opcode.swap(mp.opcode); - return cimg::type::nan(); - } - - static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector) - unsigned int - siz = (unsigned int)mp.opcode[2], - ptrs1 = (unsigned int)mp.opcode[4] + 1, - ptrs2 = (unsigned int)mp.opcode[5] + 1; - double *ptrd = &_mp_arg(1) + 1; - mp_func op = (mp_func)mp.opcode[3]; - CImg l_opcode(1,4); + mp_func op = (mp_func)mp.opcode[4]; + CImg l_opcode(mp.opcode._data + 3,nb_args + 2); + l_opcode[0] = l_opcode[1]; l_opcode.swap(mp.opcode); ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3]; - while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } + while (siz_vector-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } l_opcode.swap(mp.opcode); return cimg::type::nan(); } @@ -26431,6 +27798,38 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } + static double mp_vector_resize_ext(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const unsigned int + siz = (unsigned int)mp.opcode[2], + ow = (unsigned int)mp.opcode[4], + oh = (unsigned int)mp.opcode[5], + od = (unsigned int)mp.opcode[6], + os = (unsigned int)mp.opcode[7], + nw = (unsigned int)mp.opcode[8], + nh = (unsigned int)mp.opcode[9], + nd = (unsigned int)mp.opcode[10], + ns = (unsigned int)mp.opcode[11]; + const int + interpolation = (int)_mp_arg(12), + boundary_conditions = (int)_mp_arg(13); + const float + ax = (float)_mp_arg(14), + ay = (float)_mp_arg(15), + az = (float)_mp_arg(16), + ac = (float)_mp_arg(17); + if (siz) { // Resize vector + const double *const ptrs = &_mp_arg(3) + 1; + CImg(ptrd,nw,nh,nd,ns,true) = CImg(ptrs,ow,oh,od,os,true). + get_resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac); + } else { // Resize scalar + const double value = _mp_arg(3); + CImg(ptrd,nw,nh,nd,ns,true) = CImg(1,1,1,1,value). + resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac); + } + return cimg::type::nan(); + } + static double mp_vector_reverse(_cimg_math_parser& mp) { double *const ptrd = &_mp_arg(1) + 1; const double *const ptrs = &_mp_arg(2) + 1; @@ -26532,7 +27931,7 @@ namespace cimg_library_suffixed { _cimg_mp_vfunc(res = vec.get_stats()[3]); } - static double mp_vtos(_cimg_math_parser& mp) { + static double mp_v2s(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const unsigned int sizd = (unsigned int)mp.opcode[2], @@ -26856,7 +28255,7 @@ namespace cimg_library_suffixed { #define _cimg_create_pointwise_functions(name,func,min_size) \ CImg& name() { \ if (is_empty()) return *this; \ - cimg_openmp_for(*this,func((double)*ptr),min_size); \ + cimg_openmp_for(*this,func((typename cimg::superset::type)*ptr),min_size); \ return *this; \ } \ CImg get_##name() const { \ @@ -26902,6 +28301,15 @@ namespace cimg_library_suffixed { **/ _cimg_create_pointwise_functions(exp,std::exp,4096) + //! Compute the error function of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its error function. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(erf,std::erf,4096) + //! Compute the logarithm of each pixel value. /** Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm @@ -28065,20 +29473,19 @@ 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[out] list_outputs A pointer to a list of output images attached to the specified math formula. + \param list_images A list of images attached to the specified math formula. **/ double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); + CImgList *const list_images=0) { + return _eval(this,expression,x,y,z,c,list_images); } //! 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); + CImgList *const list_images=0) const { + return _eval(0,expression,x,y,z,c,list_images); } // Fast function to pre-evaluate common expressions. @@ -28088,25 +29495,55 @@ namespace cimg_library_suffixed { if (!expression || !*expression) { res = (t)0; return true; } const char c = *expression; bool is_success = false; - char c1, end; - double val; - if (c>='0' && c<='9') { // Possible value + char sep, end; + double val,val2; + int err; + if ((c>='0' && c<='9') || c=='.') { // Possible value if (!expression[1]) { // Single digit res = (t)(c - '0'); is_success = true; - } else if (std::sscanf(expression,"%lf%c",&val,&end)==1) { // Single value + } else if ((err = std::sscanf(expression,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // Single value res = (t)val; is_success = true; + } else if (err==3) { // Value1 Operator Value2 + switch (sep) { + case '+' : res = (t)(val + val2); is_success = true; break; + case '-' : res = (t)(val - val2); is_success = true; break; + case '*' : res = (t)(val*val2); is_success = true; break; + case '/' : res = (t)(val/val2); is_success = true; break; + case '%' : res = (t)cimg::mod(val,val2); is_success = true; break; + case '&' : res = (t)((long)val & (long)val2); is_success = true; break; + case '|' : res = (t)((long)val | (long)val2); is_success = true; break; + case '>' : res = (t)(val>val2); is_success = true; break; + case '<' : res = (t)(val='0' && c1<='0') { + (((sep = expression[1])>='0' && sep<='9') || sep=='.')) { if (!expression[2]) { // [+-!] + Single digit - const int ival = c1 - '0'; + const int ival = sep - '0'; res = (t)(c=='+'?ival:c=='-'?-ival:!ival); is_success = true; - } else if (std::sscanf(expression + 1,"%lf%c",&val,&end)==1) { // [+-!] Single value + } else if ((err = std::sscanf(expression + 1,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // [+-!] Single value res = (t)(c=='+'?val:c=='-'?-val:(double)!val); is_success = true; + } else if (err==3) { // [+-!] Value1 Operator Value2 + const double val1 = c=='+'?val:c=='-'?-val:(double)!val; + switch (sep) { + case '+' : res = (t)(val1 + val2); is_success = true; break; + case '-' : res = (t)(val1 - val2); is_success = true; break; + case '*' : res = (t)(val1*val2); is_success = true; break; + case '/' : res = (t)(val1/val2); is_success = true; break; + case '%' : res = (t)cimg::mod(val1,val2); is_success = true; break; + case '&' : res = (t)((long)val1 & (long)val2); is_success = true; break; + case '|' : res = (t)((long)val1 | (long)val2); is_success = true; break; + case '>' : res = (t)(val1>val2); is_success = true; break; + case '<' : res = (t)(val1 *const img_output, const char *const expression, const double x, const double y, const double z, const double c, - const CImgList *const list_inputs, CImgList *const list_outputs) const { + CImgList *const list_images) const { if (!expression || !*expression) return 0; double _val = 0; if (__eval(expression,_val)) return _val; _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*' || *expression==':'),"eval", - *this,img_output,list_inputs,list_outputs,false); + *this,img_output,list_images,false); mp.begin_t(); const double val = mp(x,y,z,c); mp.end_t(); @@ -28143,34 +29580,33 @@ 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[out] list_outputs A pointer to a list of output images attached to the specified math formula. + \param list_images A list of input images attached to the specified math formula. **/ template void eval(CImg &output, const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); + CImgList *const list_images=0) { + _eval(output,this,expression,x,y,z,c,list_images); } //! Evaluate math formula \const. template void eval(CImg& output, const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { - _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); + CImgList *const list_images=0) const { + _eval(output,0,expression,x,y,z,c,list_images); } template void _eval(CImg& output, CImg *const img_output, const char *const expression, const double x, const double y, const double z, const double c, - const CImgList *const list_inputs, CImgList *const list_outputs) const { + CImgList *const list_images) const { if (!expression || !*expression) { output.assign(1); *output = 0; return; } double _val = 0; if (__eval(expression,_val)) { output.assign(1); *output = _val; return; } _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*' || *expression==':'),"eval", - *this,img_output,list_inputs,list_outputs,false); + *this,img_output,list_images,false); output.assign(1,std::max(1U,mp.result_dim)); mp.begin_t(); mp(x,y,z,c,output._data); @@ -28182,36 +29618,34 @@ namespace cimg_library_suffixed { /** \param expression Math formula, as a C-string. \param xyzc Set of values (x,y,z,c) used for the evaluation. - \param list_inputs A list of input images attached to the specified math formula. - \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + \param list_images A list of input images attached to the specified math formula. **/ template CImg eval(const char *const expression, const CImg& xyzc, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - return _eval(this,expression,xyzc,list_inputs,list_outputs); + CImgList *const list_images=0) { + return _eval(this,expression,xyzc,list_images); } //! 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); + CImgList *const list_images=0) const { + return _eval(0,expression,xyzc,list_images); } 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 { + CImgList *const list_images=0) const { CImg res(1,xyzc.size()/4); if (!expression || !*expression) return res.fill(0); - _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false); + _cimg_math_parser mp(expression,"eval",*this,output,list_images,false); #if cimg_use_openmp!=0 - unsigned int tid = 0; cimg_pragma_openmp(parallel if (res._height>=512)) { - _cimg_math_parser *_mp = 0; - cimg_pragma_openmp(critical(_eval)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } - _cimg_math_parser &lmp = *_mp; + _cimg_math_parser + *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp, + &lmp = *_mp; cimg_pragma_openmp(barrier) lmp.begin_t(); cimg_pragma_openmp(for) @@ -28597,15 +30031,25 @@ namespace cimg_library_suffixed { //! Invert the instance image, viewed as a matrix. /** + If the instance matrix is not square, the Moore-Penrose pseudo-inverse is computed instead. \param use_LU Choose the inverting algorithm. Can be: - - \c true: LU-based matrix inversion. - - \c false: SVD-based matrix inversion. + - \c true: LU solver (faster but less precise). + - \c false: SVD solver (more precise but slower). + \param lambda is used only in the Moore-Penrose pseudoinverse for estimating A^t.(A^t.A + lambda.Id)^-1. **/ - CImg& invert(const bool use_LU=true) { - if (_width!=_height || _depth!=1 || _spectrum!=1) + CImg& invert(const bool use_LU=false, const float lambda=0) { + if (_depth!=1 || _spectrum!=1) throw CImgInstanceException(_cimg_instance - "invert(): Instance is not a square matrix.", + "invert(): Instance is not a matrix.", cimg_instance); + if (lambda<0) + throw CImgArgumentException(_cimg_instance + "invert(): Specified lambda (%g) should be >=0.", + cimg_instance); + + if (_width!=_height) return get_invert(use_LU,lambda).move_to(*this); // Non-square matrix: Pseudoinverse + + // Square matrix. const double dete = _width>3?-1.:det(); if (dete!=0. && _width==2) { const double @@ -28646,7 +30090,7 @@ namespace cimg_library_suffixed { if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0); delete[] IPIV; delete[] lapA; delete[] WORK; #else - if (use_LU) { // LU-based + if (use_LU) { // LU solver CImg A(*this,false), indx; bool d; A._LU(indx,d); @@ -28657,47 +30101,63 @@ namespace cimg_library_suffixed { col._solve(A,indx); cimg_forX(*this,i) (*this)(j,i) = (T)col(i); } - } else pseudoinvert(false); // SVD-based + } else _get_invert_svd(false).move_to(*this); // SVD solver #endif } return *this; } //! Invert the instance image, viewed as a matrix \newinstance. - CImg get_invert(const bool use_LU=true) const { - return CImg(*this,false).invert(use_LU); - } + CImg get_invert(const bool use_LU=false, const float lambda=0) const { + if (_depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "invert(): Instance is not a matrix.", + cimg_instance); + if (lambda<0) + throw CImgArgumentException(_cimg_instance + "invert(): Specified lambda (%g) should be >=0.", + cimg_instance); - //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix. - /** - **/ - CImg& pseudoinvert(const bool use_LU=false) { - return get_pseudoinvert(use_LU).move_to(*this); - } + if (_width==_height) return CImg(*this,false).invert(use_LU,lambda); // Square matrix - //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance. - CImg get_pseudoinvert(const bool use_LU=false) const { - - // LU-based method. + // Non-square matrix: Pseudoinverse if (use_LU) { - CImg AtA(width(),width()); - cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128)) - cimg_forY(AtA,i) - for (int j = 0; j<=i; ++j) { - double res = 0; - cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k); - AtA(j,i) = AtA(i,j) = (Tfloat)res; - } - AtA.invert(true); - return AtA*get_transpose(); + if (_width<_height) { // under-solved system -> (A^t.A)^-1.A^t + CImg AtA(width(),width()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128)) + cimg_forY(AtA,i) + for (int j = 0; j<=i; ++j) { + double res = 0; + cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k); + AtA(j,i) = AtA(i,j) = (Tfloat)res; + } + if (lambda!=0) cimg_forY(AtA,i) AtA(i,i)+=lambda; + AtA.invert(true); + return AtA*get_transpose(); + } else { // over-resolved linear system -> A^t.(A.A^t)^-1 + CImg AAt(height(),height()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128)) + cimg_forY(AAt,i) + for (int j = 0; j<=i; ++j) { + double res = 0; + cimg_forX(*this,k) res+=(*this)(k,i)*(*this)(k,j); + AAt(j,i) = AAt(i,j) = (Tfloat)res; + } + if (lambda!=0) cimg_forY(AAt,i) AAt(i,i)+=lambda; + AAt.invert(true); + return get_transpose()*AAt; + } } + return _get_invert_svd(lambda); + } - // SVD-based method. + // SVD solver, both used for inverse and pseudoinverse. + CImg _get_invert_svd(const float lambda) const { CImg U, S, V; SVD(U,S,V,false); const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max(); cimg_forX(V,x) { - const Tfloat s = S(x), invs = s>epsilon?1/s:0; + const Tfloat s = S(x), invs = lambda?1/(lambda + s):s>epsilon?1/s:0; cimg_forY(V,y) V(x,y)*=invs; } return V*U.transpose(); @@ -28707,8 +30167,8 @@ namespace cimg_library_suffixed { /** \param A Matrix of the linear system. \param use_LU In case of non square system (least-square solution), - choose between SVD-based (\c false) or LU-based (\c true) method. - LU method is faster for large matrices, but numerically less stable. + choose between SVD (\c false) or LU (\c true) solver. + LU solver is faster for large matrices, but numerically less stable. \note Solve \c AX = B where \c B=*this. **/ template @@ -28810,10 +30270,10 @@ namespace cimg_library_suffixed { INFO); assign(NRHS, N); if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l]; - else (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); + else (A.get_invert(use_LU)*(*this)).move_to(*this); delete[] lapA; delete[] lapB; delete[] WORK; #else - (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); + (A.get_invert(use_LU)*(*this)).move_to(*this); #endif } return *this; @@ -29491,7 +30951,7 @@ namespace cimg_library_suffixed { cimg_instance, dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum); - if (!method) return get_solve(dictionary,true); + if (!method) return get_solve(dictionary); CImg W(_width,dictionary._width,1,1,0); // Compute dictionary norm and normalize it. @@ -29557,7 +31017,7 @@ namespace cimg_library_suffixed { cimg_forY(sD,y) sD(sd,y) = D(d,y); inds[sd++] = d; } - S0.get_solve(sD,true).move_to(sD); // sD is now a one-column vector of weights + S0.get_solve(sD).move_to(sD); // sD is now a one-column vector of weights // Recompute residual signal. S = S0; @@ -29584,7 +31044,7 @@ namespace cimg_library_suffixed { cimg_forY(sD,y) sD(sd,y) = D(d,y); inds[sd++] = d; } - S0.get_solve(sD,true).move_to(sD); + S0.get_solve(sD).move_to(sD); cimg_forY(sD,k) W(x,inds[k]) = sD[k]; } } @@ -30030,6 +31490,70 @@ namespace cimg_library_suffixed { return r; } + //! Return a \c 1x17 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15, + const T& a16) { + CImg r(1,17); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; + return r; + } + + //! Return a \c 1x18 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15, + const T& a16, const T& a17) { + CImg r(1,18); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17; + return r; + } + + //! Return a \c 1x19 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15, + const T& a16, const T& a17, const T& a18) { + CImg r(1,19); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17; + r[18] = a18; + return r; + } + + //! Return a \c 1x20 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15, + const T& a16, const T& a17, const T& a18, const T& a19) { + CImg r(1,20); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17; + r[18] = a18; r[19] = a19; + return r; + } + + //! Return a \c 1x21 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15, + const T& a16, const T& a17, const T& a18, const T& a19, + const T& a20) { + CImg r(1,21); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17; + r[18] = a18; r[19] = a19; r[20] = a20; + return r; + } + //! Return a 1x1 matrix containing specified coefficients. /** \param a0 First matrix value. @@ -30245,8 +31769,8 @@ namespace cimg_library_suffixed { for (ptrd = _data; ptrd& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, - const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { - return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0); + CImgList *const list_images=0) { + return _fill(expression,repeat_values,allow_formula?1:0,list_images,"fill",0); } // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula | // 2 = allow formula and do not fill image values }. CImg& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode, - const CImgList *const list_inputs, CImgList *const list_outputs, - const char *const calling_function, const CImg *provides_copy) { + CImgList *const list_images, const char *const calling_function, const CImg *provides_copy) { if (is_empty() || !expression || !*expression) return *this; const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); - CImg is_error; - bool is_value_sequence = false; + CImg is_error_expr; + bool is_error_seq = false, is_value_sequence = false; cimg_abort_init; if (formula_mode) { @@ -30696,7 +32218,7 @@ namespace cimg_library_suffixed { CImg base = provides_copy?provides_copy->get_shared():get_shared(); _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*' || *expression==':'), - calling_function,base,this,list_inputs,list_outputs,true); + calling_function,base,this,list_images,true); if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && mp.need_input_copy) base.assign().assign(*this,false); // Needs input copy @@ -30715,14 +32237,21 @@ namespace cimg_library_suffixed { bool do_in_parallel = false; #if cimg_use_openmp!=0 - cimg_openmp_if(*expression=='*' || *expression==':' || - (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2)) + if (mp.is_noncritical_run && (*expression=='*' || *expression==':')) + throw CImgArgumentException(_cimg_instance + "%s(): Cannot evaluate expression '%s' in parallel, " + "as 'run()' is used outside a 'critical()' section.", + cimg_instance,calling_function,expression); + cimg_openmp_if(!mp.is_noncritical_run && + (*expression=='*' || *expression==':' || + (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2))) do_in_parallel = true; #endif if (mp.result_dim) { // Vector-valued expression const unsigned int N = std::min(mp.result_dim,_spectrum); const ulongT whd = (ulongT)_width*_height*_depth; T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; + if (*expression=='<') { CImg res(1,mp.result_dim); mp.begin_t(); @@ -30754,12 +32283,11 @@ namespace cimg_library_suffixed { } else { #if cimg_use_openmp!=0 - unsigned int tid = 0; cimg_pragma_openmp(parallel) { - _cimg_math_parser *_mp = 0; - cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } - _cimg_math_parser &lmp = *_mp; + _cimg_math_parser + *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp, + &lmp = *_mp; lmp.is_fill = true; cimg_pragma_openmp(barrier) lmp.begin_t(); @@ -30811,12 +32339,11 @@ namespace cimg_library_suffixed { } else { #if cimg_use_openmp!=0 - unsigned int tid = 0; cimg_pragma_openmp(parallel) { - _cimg_math_parser *_mp = 0; - cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } - _cimg_math_parser &lmp = *_mp; + _cimg_math_parser + *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp, + &lmp = *_mp; lmp.is_fill = true; cimg_pragma_openmp(barrier) lmp.begin_t(); @@ -30846,36 +32373,20 @@ namespace cimg_library_suffixed { } } mp.end(); - } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error_expr); } } // Try to fill values according to a value sequence. - if (!formula_mode || is_value_sequence || is_error) { - CImg item(256); - char sep = 0; - const char *nexpression = expression; - ulongT nb = 0; - const ulongT siz = size(); - T *ptrd = _data; - for (double val = 0; *nexpression && nb0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { - nexpression+=std::strlen(item) + (err>1); - *(ptrd++) = (T)val; - } else break; - } + if (!formula_mode || is_value_sequence || is_error_expr) { + is_error_seq = _fill_from_values(expression,repeat_values); cimg::exception_mode(omode); - if (nb 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?1:0,list_inputs,list_outputs); + CImgList *const list_images=0) const { + return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_images); + } + + //! Fill sequentially pixel values according to a value sequence, given as a string. + /** + \param values C-string describing a sequence of values. + \param repeat_values Tells if this sequence must be repeated when filling. + **/ + CImg& fill_from_values(const char *const values, const bool repeat_values) { + if (_fill_from_values(values,repeat_values)) + throw CImgArgumentException(_cimg_instance + "Invalid sequence of filling values '%s'.", + cimg_instance,values); + return *this; + } + + //! Fill sequentially pixel values according to a value sequence, given as a string \newinstance. + CImg get_fill_from_values(const char *const values, const bool repeat_values) const { + return (+*this).fill_from_values(values,repeat_values); + } + + // Fill image according to a value sequence, given as a string. + // Return 'true' if an error occured. + bool _fill_from_values(const char *const values, const bool repeat_values) { + CImg item(256); + const char *nvalues = values; + const ulongT siz = size(); + T *ptrd = _data; + ulongT nb = 0; + char sep = 0; + for (double val = 0; *nvalues && nb0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { + nvalues+=std::strlen(item) + (err>1); + *(ptrd++) = (T)val; + } else break; + } + if (nb hist = get_histogram(nb_levels,m,M); + ulongT sum = 0, sumB = 0, wB = 0; + double best_variance = 0; + unsigned int best_t = 0; + cimg_forX(hist,t) sum+=t*hist[t]; + cimg_forX(hist,t) { + wB+=hist[t]; + if (wB) { + const ulongT wF = size() - wB; + if (!wF) break; + sumB+=t*hist[t]; + const double + mB = (double)sumB/wB, + mF = (double)(sum - sumB)/wF, + variance = wB*wF*cimg::sqr(mB - mF); + if (variance>best_variance) { best_variance = variance; best_t = t; } + } + } + return m + best_t*(M - m)/(hist.width() - 1); + } + //! Threshold pixel values. /** \param value Threshold value @@ -31693,7 +33272,7 @@ namespace cimg_library_suffixed { return (+*this).equalize(nblevels); } - //! Index multi-valued pixels regarding to a specified colormap. + //! Index multi-valued pixels regarding to a specified palette. /** \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing. \param dithering Level of dithering (0=disable, 1=standard level). @@ -31965,34 +33544,35 @@ namespace cimg_library_suffixed { return res; } - //! Map predefined colormap on the scalar (indexed) image instance. + //! Map predefined palette on the scalar (indexed) image instance. /** - \param colormap Multi-valued colormap used for mapping the indexes. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \param palette Multi-valued palette used for mapping the indexes. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \par Example \code const CImg img("reference.jpg"), - colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), - colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), - res = img.get_index(colormap1,0).map(colormap2); + palette1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), + palette2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), + res = img.get_index(palette1,0).map(palette2); (img,res).display(); \endcode \image html ref_map.jpg **/ template - CImg& map(const CImg& colormap, const unsigned int boundary_conditions=0) { - return get_map(colormap,boundary_conditions).move_to(*this); + CImg& map(const CImg& palette, const unsigned int boundary_conditions=0) { + return get_map(palette,boundary_conditions).move_to(*this); } - //! Map predefined colormap on the scalar (indexed) image instance \newinstance. + //! Map predefined palette on the scalar (indexed) image instance \newinstance. template - CImg get_map(const CImg& colormap, const unsigned int boundary_conditions=0) const { + CImg get_map(const CImg& palette, const unsigned int boundary_conditions=0) const { const ulongT whd = (ulongT)_width*_height*_depth, siz = size(), - cwhd = (ulongT)colormap._width*colormap._height*colormap._depth, + cwhd = (ulongT)palette._width*palette._height*palette._depth, cwhd2 = 2*cwhd; - CImg res(_width,_height,_depth,_spectrum*colormap._spectrum); - switch (colormap._spectrum) { + CImg res(_width,_height,_depth,_spectrum*palette._spectrum); + switch (palette._spectrum) { case 1 : { // Optimized for scalars switch (boundary_conditions) { @@ -32000,33 +33580,33 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256)) for (longT off = 0; off<(longT)siz; ++off) { const ulongT ind = ((ulongT)_data[off])%cwhd2; - res[off] = colormap[ind& default_LUT256() { - static CImg colormap; + static CImg palette; cimg::mutex(8); - if (!colormap) { - colormap.assign(1,256,1,3); + if (!palette) { + palette.assign(1,256,1,3); for (unsigned int index = 0, r = 16; r<256; r+=32) for (unsigned int g = 16; g<256; g+=32) for (unsigned int b = 32; b<256; b+=64) { - colormap(0,index,0) = (Tuchar)r; - colormap(0,index,1) = (Tuchar)g; - colormap(0,index++,2) = (Tuchar)b; + palette(0,index,0) = (Tuchar)r; + palette(0,index,1) = (Tuchar)g; + palette(0,index++,2) = (Tuchar)b; } } cimg::mutex(8,0); - return colormap; + return palette; } - //! Return colormap \e "HSV", containing 256 colors entries in RGB. + //! Return palette \e "HSV", containing 256 colors entries in RGB. /** - \return The following \c 256x1x1x3 colormap is returned: + \return The following \c 256x1x1x3 palette is returned: \image html ref_colormap_hsv.jpg **/ static const CImg& HSV_LUT256() { - static CImg colormap; + static CImg palette; cimg::mutex(8); - if (!colormap) { + if (!palette) { CImg tmp(1,256,1,3,1); tmp.get_shared_channel(0).sequence(0,359); - colormap = tmp.HSVtoRGB(); + palette = tmp.HSVtoRGB(); } cimg::mutex(8,0); - return colormap; + return palette; } - //! Return colormap \e "lines", containing 256 colors entries in RGB. + //! Return palette \e "lines", containing 256 colors entries in RGB. /** - \return The following \c 256x1x1x3 colormap is returned: + \return The following \c 256x1x1x3 palette is returned: \image html ref_colormap_lines.jpg **/ static const CImg& lines_LUT256() { @@ -32483,91 +34063,91 @@ namespace cimg_library_suffixed { 158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97, 57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4, 73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 }; - static const CImg colormap(pal,1,256,1,3,false); - return colormap; + static const CImg palette(pal,1,256,1,3,false); + return palette; } - //! Return colormap \e "hot", containing 256 colors entries in RGB. + //! Return palette \e "hot", containing 256 colors entries in RGB. /** - \return The following \c 256x1x1x3 colormap is returned: + \return The following \c 256x1x1x3 palette is returned: \image html ref_colormap_hot.jpg **/ static const CImg& hot_LUT256() { - static CImg colormap; + static CImg palette; cimg::mutex(8); - if (!colormap) { - colormap.assign(1,4,1,3,(T)0); - colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255; - colormap.resize(1,256,1,3,3); + if (!palette) { + palette.assign(1,4,1,3,(T)0); + palette[1] = palette[2] = palette[3] = palette[6] = palette[7] = palette[11] = 255; + palette.resize(1,256,1,3,3); } cimg::mutex(8,0); - return colormap; + return palette; } - //! Return colormap \e "cool", containing 256 colors entries in RGB. + //! Return palette \e "cool", containing 256 colors entries in RGB. /** - \return The following \c 256x1x1x3 colormap is returned: + \return The following \c 256x1x1x3 palette is returned: \image html ref_colormap_cool.jpg **/ static const CImg& cool_LUT256() { - static CImg colormap; + static CImg palette; cimg::mutex(8); - if (!colormap) colormap.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3); + if (!palette) palette.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3); cimg::mutex(8,0); - return colormap; + return palette; } - //! Return colormap \e "jet", containing 256 colors entries in RGB. + //! Return palette \e "jet", containing 256 colors entries in RGB. /** - \return The following \c 256x1x1x3 colormap is returned: + \return The following \c 256x1x1x3 palette is returned: \image html ref_colormap_jet.jpg **/ static const CImg& jet_LUT256() { - static CImg colormap; + static CImg palette; cimg::mutex(8); - if (!colormap) { - colormap.assign(1,4,1,3,(T)0); - colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255; - colormap.resize(1,256,1,3,3); + if (!palette) { + palette.assign(1,4,1,3,(T)0); + palette[2] = palette[3] = palette[5] = palette[6] = palette[8] = palette[9] = 255; + palette.resize(1,256,1,3,3); } cimg::mutex(8,0); - return colormap; + return palette; } - //! Return colormap \e "flag", containing 256 colors entries in RGB. + //! Return palette \e "flag", containing 256 colors entries in RGB. /** \return The following \c 256x1x1x3 colormap is returned: - \image html ref_colormap_flag.jpg + \image html ref_palette_flag.jpg **/ static const CImg& flag_LUT256() { - static CImg colormap; + static CImg palette; cimg::mutex(8); - if (!colormap) { - colormap.assign(1,4,1,3,(T)0); - colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255; - colormap.resize(1,256,1,3,0,2); + if (!palette) { + palette.assign(1,4,1,3,(T)0); + palette[0] = palette[1] = palette[5] = palette[9] = palette[10] = 255; + palette.resize(1,256,1,3,0,2); } cimg::mutex(8,0); - return colormap; + return palette; } - //! Return colormap \e "cube", containing 256 colors entries in RGB. + //! Return palette \e "cube", containing 256 colors entries in RGB. /** - \return The following \c 256x1x1x3 colormap is returned: + \return The following \c 256x1x1x3 palette is returned: \image html ref_colormap_cube.jpg **/ static const CImg& cube_LUT256() { - static CImg colormap; + static CImg palette; cimg::mutex(8); - if (!colormap) { - colormap.assign(1,8,1,3,(T)0); - colormap[1] = colormap[3] = colormap[5] = colormap[7] = - colormap[10] = colormap[11] = colormap[12] = colormap[13] = - colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255; - colormap.resize(1,256,1,3,3); + if (!palette) { + palette.assign(1,8,1,3,(T)0); + palette[1] = palette[3] = palette[5] = palette[7] = + palette[10] = palette[11] = palette[12] = palette[13] = + palette[20] = palette[21] = palette[22] = palette[23] = 255; + palette.resize(1,256,1,3,3); } cimg::mutex(8,0); - return colormap; + return palette; } //! Convert pixel values from sRGB to RGB color spaces. @@ -33421,7 +35001,7 @@ namespace cimg_library_suffixed { case 3 : { // Mirror res.assign(sx,sy,sz,sc); const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024)) cimg_forXYZC(res,x,y,z,c) { const int mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2), @@ -33440,7 +35020,7 @@ namespace cimg_library_suffixed { z0 = ((int)zc%depth()) - depth(), c0 = ((int)cc%spectrum()) - spectrum(), dx = width(), dy = height(), dz = depth(), dc = spectrum(); - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024)) for (int c = c0; c<(int)sc; c+=dc) for (int z = z0; z<(int)sz; z+=dz) for (int y = y0; y<(int)sy; y+=dy) @@ -33577,78 +35157,88 @@ namespace cimg_library_suffixed { case 2 : { bool instance_first = true; if (sx!=_width) { - CImg tmp(sx,_height,_depth,_spectrum,0); - for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; - if (!b) { - cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width; - ++t; - b = _width; + if (sx>_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,_height,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256)) + cimg_forYZC(tmp,y,z,v) { + for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; + if (!b) { tmp(t++,y,z,v)/=_width; b = _width; } + if (!c) { ++s; c = sx; } + } } - if (!c) { ++s; c = sx; } + tmp.move_to(res); } - tmp.move_to(res); instance_first = false; } + if (sy!=_height) { - CImg tmp(sx,sy,_depth,_spectrum,0); - for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - if (instance_first) - cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; - else - cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; - if (!b) { - cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height; - ++t; - b = _height; + if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256)) + cimg_forXZC(tmp,x,z,v) { + for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; + else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; + if (!b) { tmp(x,t++,z,v)/=_height; b = _height; } + if (!c) { ++s; c = sy; } + } } - if (!c) { ++s; c = sy; } + tmp.move_to(res); } - tmp.move_to(res); instance_first = false; } + if (sz!=_depth) { - CImg tmp(sx,sy,sz,_spectrum,0); - for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - if (instance_first) - cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; - else - cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; - if (!b) { - cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth; - ++t; - b = _depth; + if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,sz,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256)) + cimg_forXYC(tmp,x,y,v) { + for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; + else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; + if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; } + if (!c) { ++s; c = sz; } + } } - if (!c) { ++s; c = sz; } + tmp.move_to(res); } - tmp.move_to(res); instance_first = false; } + if (sc!=_spectrum) { - CImg tmp(sx,sy,sz,sc,0); - for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) { - const unsigned int d = std::min(b,c); - a-=d; b-=d; c-=d; - if (instance_first) - cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; - else - cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; - if (!b) { - cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum; - ++t; - b = _spectrum; + if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res); + else { + CImg tmp(sx,sy,sz,sc,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sc>=256 && _width*_height*_depth>=256)) + cimg_forXYZ(tmp,x,y,z) { + for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; + else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; + if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; } + if (!c) { ++s; c = sc; } + } } - if (!c) { ++s; c = sc; } + tmp.move_to(res); } - tmp.move_to(res); instance_first = false; } + } break; // Linear interpolation. @@ -33677,7 +35267,8 @@ namespace cimg_library_suffixed { *(poff++) = (unsigned int)curr - (unsigned int)old; } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resx.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) cimg_forYZC(resx,y,z,c) { const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1; T *ptrd = resx.data(0,y,z,c); @@ -33712,7 +35303,8 @@ namespace cimg_library_suffixed { *(poff++) = sx*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resy.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) cimg_forXZC(resy,x,z,c) { const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx; T *ptrd = resy.data(x,0,z,c); @@ -33751,7 +35343,8 @@ namespace cimg_library_suffixed { *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resz.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) cimg_forXYC(resz,x,y,c) { const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy; T *ptrd = resz.data(x,y,0,c); @@ -33790,7 +35383,8 @@ namespace cimg_library_suffixed { *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resc.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) cimg_forXYZ(resc,x,y,z) { const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz; T *ptrd = resc.data(x,y,z,0); @@ -33905,7 +35499,8 @@ namespace cimg_library_suffixed { *(poff++) = (unsigned int)curr - (unsigned int)old; } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resx.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) cimg_forYZC(resx,y,z,c) { const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2); T *ptrd = resx.data(0,y,z,c); @@ -33947,7 +35542,8 @@ namespace cimg_library_suffixed { *(poff++) = sx*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resy.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) cimg_forXZC(resy,x,z,c) { const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx; T *ptrd = resy.data(x,0,z,c); @@ -33992,7 +35588,8 @@ namespace cimg_library_suffixed { *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resz.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) cimg_forXYC(resz,x,y,c) { const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy; T *ptrd = resz.data(x,y,0,c); @@ -34037,7 +35634,8 @@ namespace cimg_library_suffixed { *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resc.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) cimg_forXYZ(resc,x,y,z) { const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; T *ptrd = resc.data(x,y,z,0); @@ -34093,7 +35691,8 @@ namespace cimg_library_suffixed { *(poff++) = (unsigned int)curr - (unsigned int)old; } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resx.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) cimg_forYZC(resx,y,z,c) { const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, *const ptrsmax = ptrs0 + (_width - 2); @@ -34141,7 +35740,8 @@ namespace cimg_library_suffixed { *(poff++) = sx*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resy.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) cimg_forXZC(resy,x,z,c) { const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, *const ptrsmax = ptrs0 + (_height - 2)*sx; @@ -34192,7 +35792,8 @@ namespace cimg_library_suffixed { *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resz.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) cimg_forXYC(resz,x,y,c) { const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, *const ptrsmax = ptrs0 + (_depth - 2)*sxy; @@ -34243,7 +35844,8 @@ namespace cimg_library_suffixed { *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); } } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resc.size(),65536)) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) cimg_forXYZ(resc,x,y,z) { const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; @@ -34295,6 +35897,7 @@ namespace cimg_library_suffixed { \param src Reference image used for dimensions. \param interpolation_type Interpolation method. \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \param centering_x Set centering type (only if \p interpolation_type=0). \param centering_y Set centering type (only if \p interpolation_type=0). \param centering_z Set centering type (only if \p interpolation_type=0). @@ -34324,6 +35927,7 @@ namespace cimg_library_suffixed { \param disp Reference display window used for dimensions. \param interpolation_type Interpolation method. \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \param centering_x Set centering type (only if \p interpolation_type=0). \param centering_y Set centering type (only if \p interpolation_type=0). \param centering_z Set centering type (only if \p interpolation_type=0). @@ -34582,7 +36186,8 @@ namespace cimg_library_suffixed { \param delta_y Amount of displacement along the Y-axis. \param delta_z Amount of displacement along the Z-axis. \param delta_c Amount of displacement along the C-axis. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. **/ CImg& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, const unsigned int boundary_conditions=0) { @@ -34811,11 +36416,11 @@ namespace cimg_library_suffixed { if (is_empty() || !axes_order) return CImg(*this,false); CImg res; const T* ptrs = _data; - unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = {}; for (unsigned int l = 0; axes_order[l]; ++l) { int c = cimg::lowercase(axes_order[l]); if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } - else { ++n_code[c%=4]; s_code[l] = c; } + else { ++n_code[c%=4]; s_code[l] = (unsigned char)c; } } if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); @@ -35052,7 +36657,7 @@ namespace cimg_library_suffixed { \param angle Rotation angle, in degrees. \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. \param boundary_conditions Boundary conditions. - Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \note The size of the image is modified. **/ CImg& rotate(const float angle, const unsigned int interpolation=1, @@ -35260,7 +36865,7 @@ namespace cimg_library_suffixed { \param angle Rotation angle, in degrees. \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. \param boundary_conditions Boundary conditions. - Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \note Most of the time, size of the image is modified. **/ CImg rotate(const float u, const float v, const float w, const float angle, @@ -35309,7 +36914,8 @@ namespace cimg_library_suffixed { \param cy Y-coordinate of the rotation center. \param cz Z-coordinate of the rotation center. \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic | 3=mirror }. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic }. \note Most of the time, size of the image is modified. **/ CImg rotate(const float u, const float v, const float w, const float angle, @@ -37198,7 +38804,7 @@ namespace cimg_library_suffixed { \param axis Splitting axis. Can be { 'x' | 'y' | 'z' | 'c' }. \param nb Number of split parts. \note - - If \c nb==0, instance image is split into blocs of egal values along the specified axis. + - If \c nb==0, instance image is split into blocs of equal values along the specified axis. - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide. - If \c nb>0, instance image is split into \c nb blocs. **/ @@ -37207,7 +38813,7 @@ namespace cimg_library_suffixed { if (is_empty()) return res; const char _axis = cimg::lowercase(axis); - if (nb<0) { // Split by bloc size + if (nb<0) { // Split by block size const unsigned int dp = (unsigned int)(nb?-nb:1); switch (_axis) { case 'x': { @@ -37297,7 +38903,7 @@ namespace cimg_library_suffixed { } } } - } else { // Split by egal values according to specified axis + } else { // Split by equal values according to specified axis T current = *_data; switch (_axis) { case 'x' : { @@ -37415,7 +39021,7 @@ namespace cimg_library_suffixed { if ((Tt)(*this)(i)==(Tt)*values) { i1 = i; j = 0; while (i<_width && (Tt)(*this)(i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; + i-=(unsigned int)j; if (i>i1) { if (i1>i0) get_columns(i0,i1 - 1).move_to(res); if (keep_values) get_columns(i1,i - 1).move_to(res); @@ -37431,7 +39037,7 @@ namespace cimg_library_suffixed { if ((Tt)(*this)(0,i)==(Tt)*values) { i1 = i; j = 0; while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; + i-=(unsigned int)j; if (i>i1) { if (i1>i0) get_rows(i0,i1 - 1).move_to(res); if (keep_values) get_rows(i1,i - 1).move_to(res); @@ -37447,7 +39053,7 @@ namespace cimg_library_suffixed { if ((Tt)(*this)(0,0,i)==(Tt)*values) { i1 = i; j = 0; while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; + i-=(unsigned int)j; if (i>i1) { if (i1>i0) get_slices(i0,i1 - 1).move_to(res); if (keep_values) get_slices(i1,i - 1).move_to(res); @@ -37463,7 +39069,7 @@ namespace cimg_library_suffixed { if ((Tt)(*this)(0,0,0,i)==(Tt)*values) { i1 = i; j = 0; while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } - i-=j; + i-=(unsigned int)j; if (i>i1) { if (i1>i0) get_channels(i0,i1 - 1).move_to(res); if (keep_values) get_channels(i1,i - 1).move_to(res); @@ -37480,7 +39086,7 @@ namespace cimg_library_suffixed { if ((Tt)(*this)[i]==(Tt)*values) { i1 = i; j = 0; while (i=vsiz) j = 0; } - i-=j; + i-=(unsigned int)j; if (i>i1) { if (i1>i0) CImg(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res); if (keep_values) CImg(_data + i1,1,(unsigned int)(i - i1)).move_to(res); @@ -37542,22 +39148,24 @@ namespace cimg_library_suffixed { \param kernel = the correlation kernel. \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \param is_normalized = enable local normalization. - \param channel mode Channel processing mode. Can be { 0=sum inputs | 1=one-for-one | 2=expand } - \param xcenter X-coordinate of the kernel center (~0U means 'centered'). + \param channel_mode Channel processing mode. + Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }. + \param xcenter X-coordinate of the kernel center (~0U>>1 means 'centered'). + \param ycenter Y-coordinate of the kernel center (~0U>>1 means 'centered'). + \param zcenter Z-coordinate of the kernel center (~0U>>1 means 'centered'). \param xstart Starting X-coordinate of the instance image. - \param xend Ending X-coordinate of the instance image. - \param xstride Stride along the X-axis. - \param xdilation Dilation along the X-axis. - \param ycenter Y-coordinate of the kernel center (~0U means 'centered'). \param ystart Starting Y-coordinate of the instance image. - \param yend Ending Y-coordinate of the instance image. - \param ystride Stride along the Y-axis. - \param ydilation Dilation along the Y-axis. - \param zcenter Z-coordinate of the kernel center (~0U means 'centered'). \param zstart Starting Z-coordinate of the instance image. + \param xend Ending X-coordinate of the instance image. + \param yend Ending Y-coordinate of the instance image. \param zend Ending Z-coordinate of the instance image. + \param xstride Stride along the X-axis. + \param ystride Stride along the Y-axis. \param zstride Stride along the Z-axis. + \param xdilation Dilation along the X-axis. + \param ydilation Dilation along the Y-axis. \param zdilation Dilation along the Z-axis. + \param interpolation_type Can be { false=nearest | true=linear }. \note - The correlation of the image instance \p *this by the kernel \p kernel is defined to be: res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j - @@ -37566,43 +39174,56 @@ namespace cimg_library_suffixed { template CImg& correlate(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_normalized=false, const unsigned int channel_mode=1, - const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, - const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, - const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const int xcenter=(int)(~0U>>1), + const int ycenter=(int)(~0U>>1), + const int zcenter=(int)(~0U>>1), + const int xstart=0, + const int ystart=0, + const int zstart=0, + const int xend=(int)(~0U>>1), + const int yend=(int)(~0U>>1), + const int zend=(int)(~0U>>1), const float xstride=1, const float ystride=1, const float zstride=1, - const float xdilation=1, const float ydilation=1, const float zdilation=1) { + const float xdilation=1, const float ydilation=1, const float zdilation=1, + const bool interpolation_type=false) { if (is_empty() || !kernel) return *this; return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode, xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, - xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + xstride,ystride,zstride,xdilation,ydilation,zdilation, + interpolation_type).move_to(*this); } template CImg<_cimg_Ttfloat> get_correlate(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_normalized=false, const unsigned int channel_mode=1, - const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, - const unsigned int zcenter=~0U, - const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, - const unsigned int xend=~0U, const unsigned int yend=~0U, - const unsigned int zend=~0U, + const int xcenter=(int)(~0U>>1), + const int ycenter=(int)(~0U>>1), + const int zcenter=(int)(~0U>>1), + const int xstart=0, + const int ystart=0, + const int zstart=0, + const int xend=(int)(~0U>>1), + const int yend=(int)(~0U>>1), + const int zend=(int)(~0U>>1), const float xstride=1, const float ystride=1, const float zstride=1, - const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + const float xdilation=1, const float ydilation=1, const float zdilation=1, + const bool interpolation_type=false) const { return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, - xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + xstride,ystride,zstride,xdilation,ydilation,zdilation, + interpolation_type,false); } //! Correlate image by a kernel \newinstance. template CImg<_cimg_Ttfloat> _correlate(const CImg& kernel, const unsigned int boundary_conditions, const bool is_normalized, const unsigned int channel_mode, - const unsigned int xcenter, const unsigned int ycenter, const unsigned int zcenter, - const unsigned int xstart, const unsigned int ystart, const unsigned zstart, - const unsigned int xend, const unsigned int yend, const unsigned int zend, + const int xcenter, const int ycenter, const int zcenter, + const int xstart, const int ystart, const int zstart, + const int xend, const int yend, const int zend, const float xstride, const float ystride, const float zstride, const float xdilation, const float ydilation, const float zdilation, - const bool is_convolve) const { - if (is_empty() || !kernel) return *this; + const bool interpolation_type, const bool is_convolve) const { typedef _cimg_Ttfloat Ttfloat; CImg res; _cimg_abort_init_openmp; @@ -37610,7 +39231,7 @@ namespace cimg_library_suffixed { if (xstart>xend || ystart>yend || zstart>zend) throw CImgArgumentException(_cimg_instance - "%s(): Invalid xyz-start/end arguments (start = (%u,%u,%u), end = (%u,%u,%u)).", + "%s(): Invalid xyz-start/end arguments (start = (%d,%d,%d), end = (%d,%d,%d)).", cimg_instance, is_convolve?"convolve":"correlate", xstart,ystart,zstart,xend,yend,zend); @@ -37620,392 +39241,483 @@ namespace cimg_library_suffixed { cimg_instance, is_convolve?"convolve":"correlate", xstride,ystride,zstride); - const int - _xstart = (int)std::min(xstart,_width - 1), - _ystart = (int)std::min(ystart,_height - 1), - _zstart = (int)std::min(zstart,_depth - 1), - _xend = (int)std::min(xend,_width - 1), - _yend = (int)std::min(yend,_height - 1), - _zend = (int)std::min(zend,_depth - 1), - nwidth = 1 + (int)std::floor((_xend - _xstart)/xstride), - nheight = 1 + (int)std::floor((_yend - _ystart)/ystride), - ndepth = 1 + (int)std::floor((_zend + _zstart)/zstride), - _xstride = (int)cimg::round(xstride), - _ystride = (int)cimg::round(ystride), - _zstride = (int)cimg::round(zstride); - - const ulongT - res_whd = (ulongT)nwidth*nheight*ndepth, - res_size = res_whd*res._spectrum; - const bool - is_inner_parallel = res_whd>=(cimg_openmp_sizefactor)*32768, - is_outer_parallel = res_size>=(cimg_openmp_sizefactor)*32768; - cimg::unused(is_inner_parallel,is_outer_parallel); + if (is_empty() || !kernel) return *this; int - _xcenter = xcenter==~0U?kernel.width()/2 - 1 + (kernel.width()%2):(int)std::min(xcenter,kernel._width - 1), - _ycenter = ycenter==~0U?kernel.height()/2 - 1 + (kernel.height()%2):(int)std::min(ycenter,kernel._height - 1), - _zcenter = zcenter==~0U?kernel.depth()/2 - 1 + (kernel.depth()%2):(int)std::min(zcenter,kernel._depth - 1), - _xdilation = (int)cimg::round(xdilation), - _ydilation = (int)cimg::round(ydilation), - _zdilation = (int)cimg::round(zdilation); - - const bool is_int_stride_dilation = - xstride==_xstride && ystride==_ystride && zstride==_zstride && - xdilation==_xdilation && ydilation==_ydilation && zdilation==_zdilation; + _xcenter = xcenter==(int)(~0U>>1)?kernel.width()/2 - 1 + (kernel.width()%2):xcenter, + _ycenter = ycenter==(int)(~0U>>1)?kernel.height()/2 - 1 + (kernel.height()%2):ycenter, + _zcenter = zcenter==(int)(~0U>>1)?kernel.depth()/2 - 1 + (kernel.depth()%2):zcenter; + float _xdilation = xdilation, _ydilation = ydilation, _zdilation = zdilation; CImg _kernel; if (is_convolve) { // If convolution, go back to correlation - _kernel = CImg(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true). - get_mirror('x').resize(kernel,-1); - _xcenter = kernel.width() - 1 - _xcenter; - _ycenter = kernel.height() - 1 - _ycenter; - _zcenter = kernel.depth() - 1 - _zcenter; + if (kernel.size()/kernel.spectrum()<=27) { + _kernel = CImg(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true). + get_mirror('x').resize(kernel,-1); + _xcenter = kernel.width() - 1 - _xcenter; + _ycenter = kernel.height() - 1 - _ycenter; + _zcenter = kernel.depth() - _zcenter - 1; + } else { _kernel = kernel.get_shared(); _xdilation*=-1; _ydilation*=-1; _zdilation*=-1; } } else _kernel = kernel.get_shared(); - if (_kernel._width==_kernel._height && _kernel._width>1 && _kernel._height>1 && - ((_kernel._depth==1 && _kernel._width<=5) || (_kernel._depth==_kernel._width && _kernel._width<=3)) && - boundary_conditions<=1 && channel_mode && - _xcenter==_kernel.width()/2 - 1 + (_kernel.width()%2) && - _ycenter==_kernel.height()/2 - 1 + (_kernel.height()%2) && - _zcenter==_kernel.depth()/2 - 1 + (_kernel.depth()%2) && - is_int_stride_dilation && _xdilation>=0 && _ydilation>=0 && _zdilation>=0) { + const int + _xend = xend==(int)(~0U>>1)?width() - 1:xend, + _yend = yend==(int)(~0U>>1)?height() - 1:yend, + _zend = zend==(int)(~0U>>1)?depth() - 1:zend, + i_xstride = (int)cimg::round(xstride), + i_ystride = (int)cimg::round(ystride), + i_zstride = (int)cimg::round(zstride), + i_xdilation = (int)cimg::round(_xdilation), + i_ydilation = (int)cimg::round(_ydilation), + i_zdilation = (int)cimg::round(_zdilation), + res_width = _xend - xstart + 1, + res_height = _yend - ystart + 1, + res_depth = _zend - zstart + 1, + smin = std::min(spectrum(),_kernel.spectrum()), + smax = std::max(spectrum(),_kernel.spectrum()), + cend = !channel_mode?spectrum()*_kernel.spectrum():smax; + const ulongT + res_wh = (ulongT)res_width*res_height, + res_whd = res_wh*res_depth; - // Optimized versions for centered 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 kernels. - const int dw = 1 - (_kernel.width()%2), dh = 1 - (_kernel.height()%2), dd = 1 - (_kernel.depth()%2); - if (dw || dh || dd) // Force kernel size to be odd - _kernel.get_resize(_kernel.width() + dw,_kernel.height() + dh,_kernel.depth() + dd,_kernel.spectrum(), - 0,0,1,1,1).move_to(_kernel.assign()); + if (!res_whd) return CImg(); + res.assign(res_width,res_height,res_depth, + !channel_mode?_spectrum*_kernel._spectrum: + channel_mode==1?smax: + channel_mode==2?(int)std::ceil((float)smax/smin):1); + if (channel_mode>=2) res.fill(0); - if (!boundary_conditions) { // Dirichlet -> Add a 1px zero border, then use _correlate() with Neumann - const int - dx = _kernel._width==1?0:1, - dy = _kernel._height==1?0:1, - dz = _kernel._depth==1?0:1; - return get_crop(-dx,-dy,-dz,width() - 1 + dx,height() - 1 + dy,depth() - 1 + dz). - _correlate(_kernel,true,is_normalized,channel_mode,_xcenter,_ycenter,_zcenter, - _xstart + dx,_ystart + dy,_zstart + dz,_xend + dx,_yend + dy,_zend + dz, - xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + const ulongT res_siz = res_whd*res._spectrum; + const bool +#if cimg_use_openmp==1 + is_master_thread = !omp_get_thread_num(), +#else + is_master_thread = true, +#endif + is_outer_parallel = is_master_thread && + (res._spectrum>=cimg::nb_cpus() || (res_siz<=(cimg_openmp_sizefactor)*32768 && res._spectrum>1)), + is_inner_parallel = is_master_thread && + (!is_outer_parallel && res_whd>=(cimg_openmp_sizefactor)*32768), + is_int_stride_dilation = xstride==i_xstride && ystride==i_ystride && zstride==i_zstride && + _xdilation==i_xdilation && _ydilation==i_ydilation && _zdilation==i_zdilation; + cimg::unused(is_inner_parallel,is_outer_parallel); + const int + w = width(), h = height(), d = depth(), + w1 = w - 1, h1 = h - 1, d1 = d - 1, + w2 = 2*w, h2 = 2*h, d2 = 2*d; + const ulongT wh = (ulongT)w*h, whd = wh*d; - } else { // Neumann boundaries - res.assign(nwidth,nheight,ndepth,std::max(_spectrum,_kernel._spectrum)); + // Reshape kernel to enable optimizations for a few cases. + if (boundary_conditions==1 && + _kernel._width>1 && _kernel._height>1 && + ((_kernel._depth==1 && _kernel._width<=5 && _kernel._height<=5) || + (_kernel._depth<=3 && _kernel._width<=3 && _kernel._height<=3)) && + xstart>=0 && ystart>=0 && zstart>=0 && + _xend=0 && i_ydilation>=0 && i_zdilation>=0) { + const unsigned int M = cimg::max(_kernel._width,_kernel._height,_kernel._depth); + _kernel.assign(_kernel.get_resize(M + 1 - (M%2),M + 1 - (M%2),_kernel._depth>1?M + 1 - (M%2):1,-100, + 0,0, + 1,1,1),false); + _xcenter = _ycenter = (int)M/2; + if (_kernel._depth>1) _ycenter = (int)M/2; + } - switch (_kernel._depth) { - case 3 : { // 3x3x3 centered kernel - cimg_forC(res,c) { - cimg_abort_test; + // Optimized version for a few particular cases (3x3, 5x5 and 3x3x3 kernels, with a few other conditions). + if (boundary_conditions==1 && + _kernel._width==_kernel._height && + ((_kernel._depth==1 && (_kernel._width==3 || _kernel._width==5)) || + (_kernel._depth==_kernel._width && _kernel._width==3)) && + _xcenter==_kernel.width()/2 && _ycenter==_kernel.height()/2 && _zcenter==_kernel.depth()/2 && + xstart>=0 && ystart>=0 && zstart>=0 && + _xend=0 && i_ydilation>=0 && i_zdilation>=0) { + + switch (_kernel._depth) { + case 3 : { // 3x3x3 centered kernel + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + for (int c = 0; c I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum); + CImg _resu = channel_mode<=1?res.get_shared_channel(c): + CImg(res.width(),res.height(),res.depth(),1); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZ(res,X,Y,Z) { + const int + x = xstart + X, y = ystart + Y, z = zstart + Z, + px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation0?y - i_ydilation:0, ny = y + i_ydilation0?z - i_zdilation:0, nz = z + i_zdilation0?x - i_xdilation:0, nx = x + i_xdilation0?y - i_ydilation:0, ny = y + i_ydilation0?z - i_zdilation:0, nz = z + i_zdilation I = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); - const int w1 = I.width() - 1, h1 = I.height() - 1, d1 = I.depth() - 1; - CImg _res = res.get_shared_channel(c); + const CImg K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum); + CImg _resu = channel_mode<=1?res.get_shared_channel(c): + CImg(res.width(),res.height(),res.depth(),1); if (is_normalized) { const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) - cimg_forXYZ(res,X,Y,Z) { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZ(res,X,Y,z) { const int - x = _xstart + _xstride*X, y = _ystart + _ystride*Y, z = _zstart + _zstride*Z, - px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0, + nx = x + i_xdilation0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0, + ny = y + i_ydilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0, + nx = x + i_xdilation0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0, + ny = y + i_ydilation I = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); - const int w1 = I.width() - 1, h1 = I.height() - 1; - CImg _res = res.get_shared_channel(c); - if (is_normalized) { - const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + case 3 : { // 3x3 centered kernel + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + for (int c = 0; c I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum); + CImg _resu = channel_mode<=1?res.get_shared_channel(c): + CImg(res.width(),res.height(),res.depth(),1); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) cimg_forXYZ(res,X,Y,z) { - const int - x = _xstart + _xstride*X, y = _ystart + _ystride*Y, - px = x - _xdilation>0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, - nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, - ny = y + _ydilation0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, - nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, - ny = y + _ydilation0?x - i_xdilation:0, nx = x + i_xdilation0?y - i_ydilation:0, ny = y + i_ydilation0?x - i_xdilation:0, nx = x + i_xdilation0?y - i_ydilation:0, ny = y + i_ydilation I = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); - CImg _res = res.get_shared_channel(c); - const int w1 = I.width() - 1, h1 = I.height() - 1; - if (is_normalized) { - const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) - cimg_forXYZ(res,X,Y,z) { - const int - x = _xstart + _xstride*X, y = _ystart + _ystride*Y, - px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation=0 && ystart>=0 && zstart>=0 && + _xend res0 = res.get_shared_channel(0); - for (int c = 1; c I = get_crop(xstart,ystart,zstart,c%_spectrum,_xend,_yend,_zend,c%_spectrum); + if (valK!=1) I*=valK; + if (is_normalized) I.sign(); + switch (channel_mode) { + case 0 : // All + case 1 : // One for one + res.get_shared_channel(c) = I; + break; + case 2 : // Partial sum + cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=I; + break; + case 3 : // Full sum + cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=I; + break; + } } - } - } else { + } else { // Generic version + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + for (int c = 0; c I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum); + CImg _resu = channel_mode<=1?res.get_shared_channel(c): + CImg(res.width(),res.height(),res.depth(),1); + Ttfloat M = 0, M2 = 0; + if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = cimg::sqr(M); } - // Generic version for other kernels and boundary conditions. - res.assign(nwidth,nheight,ndepth, - channel_mode==0?_kernel._spectrum: - channel_mode==1?std::max(_spectrum,_kernel._spectrum): - _spectrum*_kernel._spectrum); +#define _cimg_correlate_x_int const int ix = xstart + i_xstride*x + i_xdilation*(p - _xcenter) +#define _cimg_correlate_y_int const int iy = ystart + i_ystride*y + i_ydilation*(q - _ycenter) +#define _cimg_correlate_z_int const int iz = zstart + i_zstride*z + i_zdilation*(r - _zcenter) +#define _cimg_correlate_x_float const float ix = xstart + xstride*x + _xdilation*(p - _xcenter) +#define _cimg_correlate_y_float const float iy = ystart + ystride*y + _ydilation*(q - _ycenter) +#define _cimg_correlate_z_float const float iz = zstart + zstride*z + _zdilation*(r - _zcenter) - if (!channel_mode) { // Channel mode: Sum inputs - cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) - cimg_forC(_kernel,kc) _cimg_abort_try_openmp { - cimg_abort_test; - const CImg K = _kernel.get_shared_channel(kc%_kernel._spectrum); - int w2 = 0, h2 = 0, d2 = 0; - Ttfloat M = 0, M2 = 0; - if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } - if (boundary_conditions>=3) { w2 = 2*width(); h2 = 2*height(); d2 = 2*depth(); } - res.fill(0); - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) - cimg_forXYZC(res,x,y,z,c) { - Ttfloat _val, val = 0, N = 0; +#define _cimg_correlate_x_int_dirichlet const bool is_in_x = ix>=0 && ix=0 && iy=0 && iz I = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(channel_mode==1?c%_kernel._spectrum:c/_spectrum); - int w2 = 0, h2 = 0, d2 = 0; - Ttfloat M = 0, M2 = 0; - if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } - if (boundary_conditions>=3) { w2 = 2*I.width(); h2 = 2*I.height(); d2 = 2*I.depth(); } - cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) - cimg_forXYZ(res,x,y,z) { - Ttfloat _val, val = 0, N = 0; +#define _cimg_correlate_x_int_periodic const int nix = cimg::mod(ix,w) +#define _cimg_correlate_y_int_periodic const int niy = cimg::mod(iy,h) +#define _cimg_correlate_z_int_periodic const int niz = cimg::mod(iz,d) +#define _cimg_correlate_x_float_periodic const float nix = cimg::mod(ix,w) +#define _cimg_correlate_y_float_periodic const float niy = cimg::mod(iy,h) +#define _cimg_correlate_z_float_periodic const float niz = cimg::mod(iz,d) - if (is_int_stride_dilation) - cimg_forXYZ(_kernel,p,q,r) { - const int - ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter), - iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter), - iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter); - switch (boundary_conditions) { - case 0 : _val = I.atXYZ(ix,iy,iz,0,0); break; // Dirichlet - case 1 : _val = I._atXYZ(ix,iy,iz); break; // Neumann - case 2 : _val = I(cimg::mod(ix,I.width()),cimg::mod(iy,I.height()), // Periodic - cimg::mod(iz,I.depth())); break; - default : { // Mirror - const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2); - _val = I(mx CImg& convolve(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_normalized=false, const unsigned int channel_mode=1, - const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, - const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, - const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const int xcenter=(int)(~0U>>1), + const int ycenter=(int)(~0U>>1), + const int zcenter=(int)(~0U>>1), + const int xstart=0, + const int ystart=0, + const int zstart=0, + const int xend=(int)(~0U>>1), + const int yend=(int)(~0U>>1), + const int zend=(int)(~0U>>1), const float xstride=1, const float ystride=1, const float zstride=1, - const float xdilation=1, const float ydilation=1, const float zdilation=1) { + const float xdilation=1, const float ydilation=1, const float zdilation=1, + const bool interpolation_type=false) { if (is_empty() || !kernel) return *this; return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode, xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, - xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + xstride,ystride,zstride,xdilation,ydilation,zdilation, + interpolation_type).move_to(*this); } //! Convolve image by a kernel \newinstance. template CImg<_cimg_Ttfloat> get_convolve(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_normalized=false, const unsigned int channel_mode=1, - const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, - const unsigned int zcenter=~0U, - const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, - const unsigned int xend=~0U, const unsigned int yend=~0U, - const unsigned int zend=~0U, - const float xstride=1, const float ystride=1, const float zstride=1, - const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + const int xcenter=(int)(~0U>>1), + const int ycenter=(int)(~0U>>1), + const int zcenter=(int)(~0U>>1), + const int xstart=0, + const int ystart=0, + const int zstart=0, + const int xend=(int)(~0U>>1), + const int yend=(int)(~0U>>1), + const int zend=(int)(~0U>>1), + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1, + const bool interpolation_type=false) const { return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, - xstride,ystride,zstride,xdilation,ydilation,zdilation,true); + xstride,ystride,zstride,xdilation,ydilation,zdilation, + interpolation_type,true); } //! Cumulate image values, optionally along specified axis. @@ -38142,10 +39870,11 @@ namespace cimg_library_suffixed { /** \param kernel Structuring element. \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). **/ template - CImg& erode(const CImg& kernel, const bool boundary_conditions=true, + CImg& erode(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_real=false) { if (is_empty() || !kernel) return *this; return get_erode(kernel,boundary_conditions,is_real).move_to(*this); @@ -38153,7 +39882,7 @@ namespace cimg_library_suffixed { //! Erode image by a structuring element \newinstance. template - CImg<_cimg_Tt> get_erode(const CImg& kernel, const bool boundary_conditions=true, + CImg<_cimg_Tt> get_erode(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_real=false) const { if (is_empty() || !kernel) return *this; if (!is_real && kernel==0) return CImg(width(),height(),depth(),spectrum(),0); @@ -38162,7 +39891,8 @@ namespace cimg_library_suffixed { const int mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1, - mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2, + w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(); const bool is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; @@ -38190,38 +39920,43 @@ namespace cimg_library_suffixed { } res(x,y,z,c) = min_val; } _cimg_abort_catch_openmp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { - cimg_abort_test2; - for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); - if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + Tt cval; + switch (boundary_conditions) { + case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); break; + case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); break; + case 2 : { + const int + nx = cimg::mod(x + xm,width()), + ny = cimg::mod(y + ym,height()), + nz = cimg::mod(z + zm,depth()); + cval = img(nx,ny,nz) - mval; + } break; + default : { + const int + tx = cimg::mod(x + xm,w2), + ty = cimg::mod(y + ym,h2), + tz = cimg::mod(z + zm,d2), + nx = tx=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); - if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); - if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + Tt cval; + switch (boundary_conditions) { + case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break; + case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break; + case 2 : { + const int + nx = cimg::mod(x + xm,width()), + ny = cimg::mod(y + ym,height()), + nz = cimg::mod(z + zm,depth()); + cval = img(nx,ny,nz); + } break; + default : { + const int + tx = cimg::mod(x + xm,w2), + ty = cimg::mod(y + ym,h2), + tz = cimg::mod(z + zm,d2), + nx = tx=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt min_val = cimg::type::max(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); - if (cval& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { - if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; if (sx>1 && _width>1) { // Along X-axis const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; CImg buf(L); cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) cimg_forYZC(*this,y,z,c) { T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off; T cur = *ptrs; ptrs+=off; bool is_first = true; for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} @@ -38332,7 +40074,7 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) cimg_forXZC(*this,x,z,c) { T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off; T cur = *ptrs; ptrs+=off; bool is_first = true; for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } @@ -38374,7 +40116,7 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) cimg_forXYC(*this,x,y,c) { T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off; T cur = *ptrs; ptrs+=off; bool is_first = true; for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } @@ -38433,10 +40175,11 @@ namespace cimg_library_suffixed { /** \param kernel Structuring element. \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). **/ template - CImg& dilate(const CImg& kernel, const bool boundary_conditions=true, + CImg& dilate(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_real=false) { if (is_empty() || !kernel) return *this; return get_dilate(kernel,boundary_conditions,is_real).move_to(*this); @@ -38444,7 +40187,7 @@ namespace cimg_library_suffixed { //! Dilate image by a structuring element \newinstance. template - CImg<_cimg_Tt> get_dilate(const CImg& kernel, const bool boundary_conditions=true, + CImg<_cimg_Tt> get_dilate(const CImg& kernel, const unsigned int boundary_conditions=1, const bool is_real=false) const { if (is_empty() || !kernel || (!is_real && kernel==0)) return *this; typedef _cimg_Tt Tt; @@ -38452,7 +40195,8 @@ namespace cimg_library_suffixed { const int mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2, mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1, - mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2, + w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(); const bool is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; @@ -38480,38 +40224,44 @@ namespace cimg_library_suffixed { } res(x,y,z,c) = max_val; } _cimg_abort_catch_openmp2 - if (boundary_conditions) - cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) - cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { - cimg_abort_test2; - for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); - if (cval>max_val) max_val = cval; + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + Tt cval; + switch (boundary_conditions) { + case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); break; + case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); break; + case 2 : { + const int + nx = cimg::mod(x + xm,width()), + ny = cimg::mod(y + ym,height()), + nz = cimg::mod(z + zm,depth()); + cval = img(nx,ny,nz) + mval; + } break; + default : { + const int + tx = cimg::mod(x + xm,w2), + ty = cimg::mod(y + ym,h2), + tz = cimg::mod(z + zm,d2), + nx = tx=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) { - const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); - if (cval>max_val) max_val = cval; } - res(x,y,z,c) = max_val; - } - } _cimg_abort_catch_openmp2 + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } else { // Binary dilation cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) for (int z = mz1; z=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); - if (cval>max_val) max_val = cval; + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + Tt cval; + switch (boundary_conditions) { + case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break; + case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break; + case 2 : { + const int + nx = cimg::mod(x + xm,width()), + ny = cimg::mod(y + ym,height()), + nz = cimg::mod(z + zm,depth()); + cval = img(nx,ny,nz); + } break; + default : { + const int + tx = cimg::mod(x + xm,w2), + ty = cimg::mod(y + ym,h2), + tz = cimg::mod(z + zm,d2), + nx = tx=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { - Tt max_val = cimg::type::min(); - for (int zm = -mz1; zm<=mz2; ++zm) - for (int ym = -my1; ym<=my2; ++ym) - for (int xm = -mx1; xm<=mx2; ++xm) - if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); - if (cval>max_val) max_val = cval; } - res(x,y,z,c) = max_val; - } - } _cimg_abort_catch_openmp2 + if (cval>max_val) max_val = cval; + } + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } } _cimg_abort_catch_openmp cimg_abort_test; @@ -38573,14 +40330,14 @@ namespace cimg_library_suffixed { \param sz Depth of the structuring element. **/ CImg& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { - if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; if (sx>1 && _width>1) { // Along X-axis const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; CImg buf(L); cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) cimg_forYZC(*this,y,z,c) { T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off; T cur = *ptrs; ptrs+=off; bool is_first = true; for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } @@ -38622,7 +40379,7 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) cimg_forXZC(*this,x,z,c) { T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off; T cur = *ptrs; ptrs+=off; bool is_first = true; for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } @@ -38664,7 +40421,7 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) cimg_forXYC(*this,x,y,c) { T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; - const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off; T cur = *ptrs; ptrs+=off; bool is_first = true; for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } @@ -38719,6 +40476,164 @@ namespace cimg_library_suffixed { return (+*this).dilate(s); } + //! Apply morphological closing by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \param is_real Do the closing in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& closing(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_real=false) { + const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth(); + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + return get_closing(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Apply morphological closing by a structuring element \newinstance. + template + CImg get_closing(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_real=false) const { + const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth(); + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2; + CImg res; + if (_depth>1) { // 3D + get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,boundary_conditions,0.5,0.5,0.5). + dilate(kernel,1,is_real).erode(kernel,1,is_real). + crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res); + } else if (_height>1) { // 2D + get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,boundary_conditions,0.5,0.5). + dilate(kernel,1,is_real).erode(kernel,1,is_real). + crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res); + } else if (_width>1) { // 1D + get_resize(width() + sx + 1,1,1,spectrum(),0,boundary_conditions,0.5). + dilate(kernel,1,is_real).erode(kernel,1,is_real). + crop(sx1 + 1,sx1 + width()).move_to(res); + } + return res; + } + + //! Apply morphological closing by a rectangular structuring element of specified size. + CImg& closing(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + return get_closing(sx,sy,sz).move_to(*this); + } + + //! Apply morphological closing by a rectangular structuring element of specified size \newinstance. + CImg get_closing(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2; + CImg res; + if (_depth>1) { // 3D + get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,1,0.5,0.5,0.5). + dilate(sx,sy,sz).erode(sx,sy,sz). + crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res); + } else if (_height>1) { // 2D + get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,1,0.5,0.5). + dilate(sx,sy).erode(sx,sy). + crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res); + } else if (_width>1) { // 1D + get_resize(width() + sx + 1,1,1,spectrum(),0,1,0.5). + dilate(sx,1).erode(sx,1). + crop(sx1 + 1,sx1 + width()).move_to(res); + } + return res; + } + + //! Apply morphological closing by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& closing(const unsigned int s) { + return closing(s,s,s); + } + + //! Apply morphological closing by a square structuring element of specified size \newinstance. + CImg get_closing(const unsigned int s) const { + return (+*this).closing(s); + } + + //! Apply morphological opening by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \param is_real Do the opening in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& opening(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_real=false) { + const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth(); + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + return get_opening(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Apply morphological opening by a structuring element \newinstance. + template + CImg get_opening(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_real=false) const { + const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth(); + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2; + CImg res; + if (_depth>1) { // 3D + get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,boundary_conditions,0.5,0.5,0.5). + erode(kernel,1,is_real).dilate(kernel,1,is_real). + crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res); + } else if (_height>1) { // 2D + get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,boundary_conditions,0.5,0.5). + erode(kernel,1,is_real).dilate(kernel,1,is_real). + crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res); + } else if (_width>1) { // 1D + get_resize(width() + sx + 1,1,1,spectrum(),0,boundary_conditions,0.5). + erode(kernel,1,is_real).dilate(kernel,1,is_real). + crop(sx1 + 1,sx1 + width()).move_to(res); + } + return res; + } + + //! Apply morphological opening by a rectangular structuring element of specified size. + CImg& opening(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + return get_opening(sx,sy,sz).move_to(*this); + } + + //! Apply morphological opening by a rectangular structuring element of specified size \newinstance. + CImg get_opening(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this; + const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2; + CImg res; + if (_depth>1) { // 3D + get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,1,0.5,0.5,0.5). + erode(sx,sy,sz).dilate(sx,sy,sz). + crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res); + } else if (_height>1) { // 2D + get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,1,0.5,0.5). + erode(sx,sy).dilate(sx,sy). + crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res); + } else if (_width>1) { // 1D + get_resize(width() + sx + 1,1,1,spectrum(),0,1,0.5). + erode(sx,1).dilate(sx,1). + crop(sx1 + 1,sx1 + width()).move_to(res); + } + return res; + } + + //! Apply morphological opening by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& opening(const unsigned int s) { + return opening(s,s,s); + } + + //! Apply morphological opening by a square structuring element of specified size \newinstance. + CImg get_opening(const unsigned int s) const { + return (+*this).opening(s); + } + //! Compute watershed transform. /** \param priority Priority map. @@ -38918,10 +40833,11 @@ namespace cimg_library_suffixed { \param sigma Standard deviation of the filter. \param order Order of the filter. Can be { 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }. \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. **/ CImg& deriche(const float sigma, const unsigned int order=0, const char axis='x', - const bool boundary_conditions=true) { + const unsigned int boundary_conditions=1) { #define _cimg_deriche_apply \ CImg Y(N); \ double *ptrY = Y._data, yb = 0, yp = 0; \ @@ -38941,13 +40857,46 @@ namespace cimg_library_suffixed { xa = xn; xn = xc; ya = yn; yn = yc; \ *ptrX = (T)(*(--ptrY)+yc); \ } + + if (order>2) + throw CImgArgumentException(_cimg_instance + "deriche(): Invalid specified order '%d' " + "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", + cimg_instance, + order); + const char naxis = cimg::lowercase(axis); - const double 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 (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c') + throw CImgArgumentException(_cimg_instance + "deriche(): Invalid specified axis '%c'.", + cimg_instance, + axis); + const double + nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width: + naxis=='y'?_height: + naxis=='z'?_depth:_spectrum)/100, + nnsigma = nsigma<0.1f?0.1f:nsigma; + + if (is_empty() || (nsigma<0.1f && !order)) return *this; + if (boundary_conditions>1) { + const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma); + switch (naxis) { + case 'x' : + return draw_image(get_resize(w + 2*border,h,d,s,0,boundary_conditions,0.5). + deriche(nnsigma,order,naxis,1).columns(border,w - 1 + border)); + case 'y' : + return draw_image(get_resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5). + deriche(nnsigma,order,naxis,1).rows(border,h - 1 + border)); + case 'z' : + return draw_image(get_resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5). + deriche(nnsigma,order,naxis,1).slices(border,d - 1 + border)); + default : + return draw_image(get_resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5). + deriche(nnsigma,order,naxis,1).channels(border,d - 1 + border)); + } + } + const double - nnsigma = nsigma<0.1f?0.1f:nsigma, alpha = 1.695f/nnsigma, ema = std::exp(-alpha), ema2 = std::exp(-2*alpha), @@ -38968,7 +40917,7 @@ namespace cimg_library_suffixed { a1 = k*ema; a2 = -a1; } break; - case 2 : { + default : { const double ea = std::exp(-alpha), k = -(ema2 - 1)/(2*alpha*ema), @@ -38978,13 +40927,8 @@ namespace cimg_library_suffixed { a2 = kn*(1 - k*alpha)*ema; a3 = -kn*ema2; } break; - default : - throw CImgArgumentException(_cimg_instance - "deriche(): Invalid specified filter order %u " - "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", - cimg_instance, - order); } + coefp = (a0 + a1)/(1 + b1 + b2); coefn = (a2 + a3)/(1 + b1 + b2); switch (naxis) { @@ -39022,7 +40966,7 @@ namespace cimg_library_suffixed { //! Apply recursive Deriche filter \newinstance. CImg get_deriche(const float sigma, const unsigned int order=0, const char axis='x', - const bool boundary_conditions=true) const { + const unsigned int boundary_conditions=1) const { return CImg(*this,false).deriche(sigma,order,axis,boundary_conditions); } @@ -39033,12 +40977,13 @@ namespace cimg_library_suffixed { \param N size of the data \param off the offset between two data point \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann }. \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005). */ static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off, const unsigned int order, const bool boundary_conditions) { - double val[4] = { 0 }; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..] + double val[4] = {}; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..] const double sumsq = filter[0], sum = sumsq * sumsq, a1 = filter[1], a2 = filter[2], a3 = filter[3], @@ -39181,7 +41126,8 @@ namespace cimg_library_suffixed { \param sigma standard deviation of the Gaussian filter \param order the order of the filter 0,1,2,3 \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \note dirichlet boundary condition has a strange behavior I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering. @@ -39195,15 +41141,51 @@ namespace cimg_library_suffixed { vol. 54, pp. 2365-2367, 2006. **/ CImg& vanvliet(const float sigma, const unsigned int order, const char axis='x', - const bool boundary_conditions=true) { - if (is_empty()) return *this; + const unsigned int boundary_conditions=1) { + + if (order>2) + throw CImgArgumentException(_cimg_instance + "deriche(): Invalid specified order '%d' " + "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", + cimg_instance, + order); + + const char naxis = cimg::lowercase(axis); + if (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c') + throw CImgArgumentException(_cimg_instance + "deriche(): Invalid specified axis '%c'.", + cimg_instance, + axis); + const double + nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width: + naxis=='y'?_height: + naxis=='z'?_depth:_spectrum)/100, + nnsigma = nsigma<0.5f?0.5f:nsigma; + + if (is_empty() || (nsigma<0.1f && !order)) return *this; + if (nsigma<0.5f) return deriche(nsigma,order,axis,boundary_conditions); if (!cimg::type::is_float()) return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this); - const char naxis = cimg::lowercase(axis); - const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; - if (is_empty() || (nsigma<0.5f && !order)) return *this; + + if (boundary_conditions>1) { + const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma); + switch (naxis) { + case 'x' : + return draw_image(get_resize(w + 2*border,h,d,s,0,boundary_conditions,0.5). + vanvliet(nnsigma,order,naxis,1).columns(border,w - 1 + border)); + case 'y' : + return draw_image(get_resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5). + vanvliet(nnsigma,order,naxis,1).rows(border,h - 1 + border)); + case 'z' : + return draw_image(get_resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5). + vanvliet(nnsigma,order,naxis,1).slices(border,d - 1 + border)); + default : + return draw_image(get_resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5). + vanvliet(nnsigma,order,naxis,1).channels(border,d - 1 + border)); + } + } + const double - nnsigma = nsigma<0.5f?0.5f:nsigma, m0 = 1.16680, m1 = 1.10783, m2 = 1.40586, m1sq = m1 * m1, m2sq = m2 * m2, q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)), @@ -39248,7 +41230,7 @@ namespace cimg_library_suffixed { //! Blur image using Van Vliet recursive Gaussian filter. \newinstance. CImg get_vanvliet(const float sigma, const unsigned int order, const char axis='x', - const bool boundary_conditions=true) const { + const unsigned int boundary_conditions=1) const { return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions); } @@ -39257,7 +41239,8 @@ namespace cimg_library_suffixed { \param sigma_x Standard deviation of the blur, along the X-axis. \param sigma_y Standard deviation of the blur, along the Y-axis. \param sigma_z Standard deviation of the blur, along the Z-axis. - \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel. \note - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian). @@ -39265,7 +41248,7 @@ namespace cimg_library_suffixed { \see deriche(), vanvliet(). **/ CImg& blur(const float sigma_x, const float sigma_y, const float sigma_z, - const bool boundary_conditions=true, const bool is_gaussian=true) { + const unsigned int boundary_conditions=1, const bool is_gaussian=true) { if (is_empty()) return *this; if (is_gaussian) { if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions); @@ -39281,24 +41264,26 @@ namespace cimg_library_suffixed { //! Blur image \newinstance. CImg get_blur(const float sigma_x, const float sigma_y, const float sigma_z, - const bool boundary_conditions=true, const bool is_gaussian=true) const { + const unsigned int boundary_conditions=1, const bool is_gaussian=true) const { return CImg(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian); } //! Blur image isotropically. /** \param sigma Standard deviation of the blur. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.a \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise. \see deriche(), vanvliet(). **/ - CImg& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) { + CImg& blur(const float sigma, const unsigned int boundary_conditions=1, const bool is_gaussian=true) { const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian); } //! Blur image isotropically \newinstance. - CImg get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) const { + CImg get_blur(const float sigma, const unsigned int boundary_conditions=1, + const bool is_gaussian=true) const { return CImg(*this,false).blur(sigma,boundary_conditions,is_gaussian); } @@ -39792,11 +41777,14 @@ namespace cimg_library_suffixed { \param boxsize Size of the box filter (can be subpixel). \param off the offset between two data point \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. */ static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off, - const int order, const bool boundary_conditions, + const int order, const unsigned int boundary_conditions, const unsigned int nb_iter) { + const int nboundary_conditions = boundary_conditions>1 && boxsize<=3?1:boundary_conditions; + // Smooth. if (boxsize>1 && nb_iter) { const int w2 = (int)(boxsize - 1)/2; @@ -39806,13 +41794,13 @@ namespace cimg_library_suffixed { for (unsigned int iter = 0; iter=N) return boundary_conditions?ptr[(N - 1)*off]:T(); - return ptr[x*off]; + const unsigned int boundary_conditions, const int x) { + switch (boundary_conditions) { + case 0 : // Dirichlet + return x<0 || x>=N?(T)0:ptr[x*off]; + case 1 : { // Neumann + const int nx = x<0?0:x>=N?N - 1:x; + return ptr[nx*off]; + } + case 2 : { // Periodic + const int nx = cimg::mod(x,N); + return ptr[nx*off]; + } + default : { // Mirror + const int + N2 = 2*N, + tx = cimg::mod(x,N2), + nx = tx{ 'x' | 'y' | 'z' | 'c' }. - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. \param nb_iter Number of filter iterations. **/ CImg& boxfilter(const float boxsize, const int order, const char axis='x', - const bool boundary_conditions=true, + const unsigned int boundary_conditions=1, const unsigned int nb_iter=1) { - if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this; const char naxis = cimg::lowercase(axis); const float nboxsize = boxsize>=0?boxsize:-boxsize* (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || !nboxsize || (nboxsize<=1 && !order)) return *this; switch (naxis) { case 'x' : { cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && @@ -39916,7 +41922,7 @@ namespace cimg_library_suffixed { // Apply box filter of order 0,1 or 2 \newinstance. CImg get_boxfilter(const float boxsize, const int order, const char axis='x', - const bool boundary_conditions=true, + const unsigned int boundary_conditions=1, const unsigned int nb_iter=1) const { return CImg(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter); } @@ -39926,14 +41932,15 @@ namespace cimg_library_suffixed { \param boxsize_x Size of the box window, along the X-axis (can be subpixel). \param boxsize_y Size of the box window, along the Y-axis (can be subpixel). \param boxsize_z Size of the box window, along the Z-axis (can be subpixel). - \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param boundary_conditions Boundary conditions. + Can be { false=dirichlet | true=neumann | 2=periodic | 3=mirror }. \param nb_iter Number of filter iterations. \note - This is a recursive algorithm, not depending on the values of the box kernel size. \see blur(). **/ CImg& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, - const bool boundary_conditions=true, + const unsigned int boundary_conditions=1, const unsigned int nb_iter=1) { if (is_empty()) return *this; if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter); @@ -39944,23 +41951,24 @@ namespace cimg_library_suffixed { //! Blur image with a box filter \newinstance. CImg get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, - const bool boundary_conditions=true) const { + const unsigned int boundary_conditions=1) const { return CImg(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions); } //! Blur image with a box filter. /** \param boxsize Size of the box window (can be subpixel). - \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.a \see deriche(), vanvliet(). **/ - CImg& blur_box(const float boxsize, const bool boundary_conditions=true) { + CImg& blur_box(const float boxsize, const unsigned int boundary_conditions=1) { const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100; return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions); } //! Blur image with a box filter \newinstance. - CImg get_blur_box(const float boxsize, const bool boundary_conditions=true) const { + CImg get_blur_box(const float boxsize, const unsigned int boundary_conditions=1) const { return CImg(*this,false).blur_box(boxsize,boundary_conditions); } @@ -40038,8 +42046,8 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ firstprivate(P,Q)) \ - cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ - cimg_abort_test; \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \ + cimg_abort_test2; \ cimg_def##N##x##N##x##N(res,x,y,z); \ tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ @@ -40058,14 +42066,14 @@ namespace cimg_library_suffixed { } \ if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ - } _cimg_abort_catch_openmp } + } _cimg_abort_catch_openmp2 } #define _cimg_blur_patch3d(N) { \ cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ firstprivate(P,Q)) \ - cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ - cimg_abort_test; \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \ + cimg_abort_test2; \ cimg_def##N##x##N##x##N(res,x,y,z); \ tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ @@ -40085,13 +42093,13 @@ namespace cimg_library_suffixed { sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \ if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ - } _cimg_abort_catch_openmp } + } _cimg_abort_catch_openmp2 } #define _cimg_blur_patch2d_fast(N) { \ cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ firstprivate(P,Q)) \ - cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ - cimg_abort_test; \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \ + cimg_abort_test2; \ cimg_def##N##x##N(res,x,y); \ tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ @@ -40109,13 +42117,13 @@ namespace cimg_library_suffixed { } \ if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ - } _cimg_abort_catch_openmp } + } _cimg_abort_catch_openmp2 } #define _cimg_blur_patch2d(N) { \ cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ firstprivate(P,Q)) \ - cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ - cimg_abort_test; \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \ + cimg_abort_test2; \ cimg_def##N##x##N(res,x,y); \ tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ @@ -40134,7 +42142,7 @@ namespace cimg_library_suffixed { sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \ if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ - } _cimg_abort_catch_openmp } + } _cimg_abort_catch_openmp2 } typedef _cimg_tfloat tfloat; if (!is_sameXYZ(guide)) @@ -40176,8 +42184,8 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) firstprivate(P,Q)) - cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Fast - cimg_abort_test; + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Fast + cimg_abort_test2; P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; @@ -40194,13 +42202,13 @@ namespace cimg_library_suffixed { } if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); - } _cimg_abort_catch_openmp + } _cimg_abort_catch_openmp2 } else { cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) firstprivate(P,Q)) - cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Exact - cimg_abort_test; + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Exact + cimg_abort_test2; P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; @@ -40218,7 +42226,7 @@ namespace cimg_library_suffixed { sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); - } _cimg_abort_catch_openmp + } _cimg_abort_catch_openmp2 } } } else switch (patch_size) { // 2D @@ -40235,8 +42243,8 @@ namespace cimg_library_suffixed { if (is_fast_approx) { cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) firstprivate(P,Q)) - cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Fast - cimg_abort_test; + cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Fast + cimg_abort_test2; P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; tfloat sum_weights = 0; @@ -40252,12 +42260,12 @@ namespace cimg_library_suffixed { } if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); - } _cimg_abort_catch_openmp + } _cimg_abort_catch_openmp2 } else { cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) firstprivate(P,Q)) - cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Exact - cimg_abort_test; + cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Exact + cimg_abort_test2; P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; tfloat sum_weights = 0, weight_max = 0; @@ -40274,10 +42282,11 @@ namespace cimg_library_suffixed { sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); - } _cimg_abort_catch_openmp + } _cimg_abort_catch_openmp2 } } } + cimg_abort_test; return res.cut(val_min,val_max); } @@ -40584,7 +42593,7 @@ namespace cimg_library_suffixed { **/ CImgList get_gradient(const char *const axes=0, const int scheme=0) const { CImgList res; - char __axes[4] = { 0 }; + char __axes[4] = {}; const char *_axes = axes?axes:__axes; if (!axes) { unsigned int k = 0; @@ -40705,7 +42714,7 @@ namespace cimg_library_suffixed { **/ CImgList get_hessian(const char *const axes=0) const { CImgList res; - char __axes[12] = { 0 }; + char __axes[12] = {}; const char *_axes = axes?axes:__axes; if (!axes) { unsigned int k = 0; @@ -40850,12 +42859,12 @@ namespace cimg_library_suffixed { ix = (Incc - Ipcc)/2, iy = (Icnc - Icpc)/2, iz = (Iccn - Iccp)/2; - *(ptrd0++)+=ix*ix; - *(ptrd1++)+=ix*iy; - *(ptrd2++)+=ix*iz; - *(ptrd3++)+=iy*iy; - *(ptrd4++)+=iy*iz; - *(ptrd5++)+=iz*iz; + cimg_pragma_openmp(atomic) *(ptrd0++)+=ix*ix; + cimg_pragma_openmp(atomic) *(ptrd1++)+=ix*iy; + cimg_pragma_openmp(atomic) *(ptrd2++)+=ix*iz; + cimg_pragma_openmp(atomic) *(ptrd3++)+=iy*iy; + cimg_pragma_openmp(atomic) *(ptrd4++)+=iy*iz; + cimg_pragma_openmp(atomic) *(ptrd5++)+=iz*iz; } } } else { // Forward/backward finite differences @@ -40868,15 +42877,15 @@ namespace cimg_library_suffixed { CImg_3x3x3(I,Tfloat); cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { const Tfloat - ixf = Incc - Iccc, ixb = Iccc - Ipcc, - iyf = Icnc - Iccc, iyb = Iccc - Icpc, - izf = Iccn - Iccc, izb = Iccc - Iccp; - *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; - *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2; - *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; - *(ptrd5++)+=(izf*izf + izb*izb)/2; + ixf = Incc - Iccc, ixb = Iccc - Ipcc, ixc = (Incc - Ipcc)/2, + iyf = Icnc - Iccc, iyb = Iccc - Icpc, iyc = (Icnc - Icpc)/2, + izf = Iccn - Iccc, izb = Iccc - Iccp, izc = (Iccn - Iccp)/2; + cimg_pragma_openmp(atomic) *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + cimg_pragma_openmp(atomic) *(ptrd1++)+=ixc*iyc; + cimg_pragma_openmp(atomic) *(ptrd2++)+=ixc*izc; + cimg_pragma_openmp(atomic) *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2; + cimg_pragma_openmp(atomic) *(ptrd4++)+=iyc*izc; + cimg_pragma_openmp(atomic) *(ptrd5++)+=(izf*izf + izb*izb)/2; } } } @@ -40892,9 +42901,9 @@ namespace cimg_library_suffixed { const Tfloat ix = (Inc - Ipc)/2, iy = (Icn - Icp)/2; - *(ptrd0++)+=ix*ix; - *(ptrd1++)+=ix*iy; - *(ptrd2++)+=iy*iy; + cimg_pragma_openmp(atomic) *(ptrd0++)+=ix*ix; + cimg_pragma_openmp(atomic) *(ptrd1++)+=ix*iy; + cimg_pragma_openmp(atomic) *(ptrd2++)+=iy*iy; } } } else { // Forward/backward finite differences (version 2) @@ -40905,11 +42914,11 @@ namespace cimg_library_suffixed { CImg_3x3(I,Tfloat); cimg_for3x3(*this,x,y,0,c,I,Tfloat) { const Tfloat - ixf = Inc - Icc, ixb = Icc - Ipc, - iyf = Icn - Icc, iyb = Icc - Icp; - *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; + ixf = Inc - Icc, ixb = Icc - Ipc, ixc = (Inc - Ipc)/2, + iyf = Icn - Icc, iyb = Icc - Icp, iyc = (Icn - Icp)/2; + cimg_pragma_openmp(atomic) *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + cimg_pragma_openmp(atomic) *(ptrd1++)+=ixc*iyc; + cimg_pragma_openmp(atomic) *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; } } } @@ -41422,17 +43431,18 @@ namespace cimg_library_suffixed { CImg a_map(_width,_height,_depth,patch_image._depth>1?3:2); CImg is_updated(_width,_height,_depth,1,3); - CImg score(_width,_height,_depth); - CImg occ; + CImg score(_width,_height,_depth), penalty; const float _patch_penalization = cimg::abs(patch_penalization); const bool allow_identity = patch_penalization>=0; - if (_patch_penalization!=0) occ.assign(patch_image._width,patch_image._height,patch_image._depth,1,0); + if (_patch_penalization!=0) + penalty.assign(patch_image._width,patch_image._height,patch_image._depth,1,0); + 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; - // Interleave image buffers to speed up patch comparison (cache-friendly). + // Interleave image buffers to speed up patch comparison (more cache-friendly). CImg in_this = get_permute_axes("cxyz"); in_this._width = _width*_spectrum; in_this._height = _height; @@ -41460,7 +43470,7 @@ namespace cimg_library_suffixed { a_map(x,y,z,0) = u; a_map(x,y,z,1) = v; a_map(x,y,z,2) = w; - score(x,y,z) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum, + score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum, x - cx1,y - cy1,z - cz1, u - cx1,v - cy1,w - cz1, u,v,w,0,allow_identity,cimg::type::inf()); @@ -41481,7 +43491,7 @@ namespace cimg_library_suffixed { a_map(x,y,z,0) = u; a_map(x,y,z,1) = v; a_map(x,y,z,2) = w; - score(x,y,z) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum, + score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum, x - cx1,y - cy1,z - cz1, u - cx1,v - cy1,w - cz1, u,v,w,0,allow_identity,cimg::type::inf()); @@ -41493,11 +43503,10 @@ namespace cimg_library_suffixed { cimg_abort_init; for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && - iter=(cimg_openmp_sizefactor)*64)) { cimg_uint64 rng = (cimg::_rand(),cimg::rng()); #if cimg_use_openmp!=0 @@ -41522,79 +43531,79 @@ namespace cimg_library_suffixed { const float best_score0 = score(x,y,z); float best_score = best_score0, s; - if (x>0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor + if (is_forward && x>0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor u = a_map(x - 1,y,z,0); v = a_map(x - 1,y,z,1); w = a_map(x - 1,y,z,2); if (u>=cx1 - 1 && u=cy1 && v=cz1 && w0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor + if (is_forward && y>0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor u = a_map(x,y - 1,z,0); v = a_map(x,y - 1,z,1); w = a_map(x,y - 1,z,2); if (u>=cx1 && u=cy1 - 1 && v=cz1 && w0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor + if (is_forward && z>0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor u = a_map(x,y,z - 1,0); v = a_map(x,y,z - 1,1); w = a_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=(cimg_openmp_sizefactor)*64)) + cimg_forXYZ(score,x,y,z) { + const float p_score = score(x,y,z); + const int + cx1 = x<=psizew1?x:(x::inf()); + if (n_score!=p_score) { score(x,y,z) = n_score; is_updated(x,y) = 3; } + } } } else { // 2D version @@ -41651,7 +43681,7 @@ namespace cimg_library_suffixed { v = cimg::cut((int)guide(x,y,1),cy1,patch_image.height() - 1 - cy2); a_map(x,y,0) = u; a_map(x,y,1) = v; - score(x,y) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum, + score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum, x - cx1,y - cy1,u - cx1,v - cy1, u,v,0,allow_identity,cimg::type::inf()); } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) { @@ -41669,7 +43699,7 @@ namespace cimg_library_suffixed { v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)); a_map(x,y,0) = u; a_map(x,y,1) = v; - score(x,y) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum, + score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum, x - cx1,y - cy1,u - cx1,v - cy1, u,v,0,allow_identity,cimg::type::inf()); } @@ -41680,11 +43710,10 @@ namespace cimg_library_suffixed { cimg_abort_init; for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && - iter=(cimg_openmp_sizefactor)*64)) { cimg_uint64 rng = (cimg::_rand(),cimg::rng()); #if cimg_use_openmp!=0 @@ -41706,45 +43735,45 @@ namespace cimg_library_suffixed { const float best_score0 = score(x,y); float best_score = best_score0, s; - if (x>0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor + if (is_forward && x>0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor u = a_map(x - 1,y,0); v = a_map(x - 1,y,1); if (u>=cx1 - 1 && u=cy1 && v0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor + if (is_forward && y>0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor u = a_map(x,y - 1,0); v = a_map(x,y - 1,1); if (u>=cx1 && u=cy1 - 1 && v=cx1 + 1 && u=cy1 && v=cx1 && u=cy1 + 1 && v=(cimg_openmp_sizefactor)*64)) + cimg_forXY(score,x,y) { + const float p_score = score(x,y); + const int + cx1 = x<=psizew1?x:(x::inf()); + if (n_score!=p_score) { score(x,y) = n_score; is_updated(x,y) = 3; } + } } } @@ -41790,7 +43837,7 @@ namespace cimg_library_suffixed { } // Compute SSD between two patches in different images. - static float _matchpatch(const CImg& img1, const CImg& img2, const CImg& occ, + static float _matchpatch(const CImg& img1, const CImg& img2, const CImg& penalty, const unsigned int psizew, const unsigned int psizeh, const unsigned int psized, const unsigned int psizec, const int x1, const int y1, const int z1, @@ -41799,7 +43846,7 @@ namespace cimg_library_suffixed { const float patch_penalization, const bool allow_identity, const float max_score) { // 3D version - if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2,(float)z1-z2)::inf(); const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2); const unsigned int psizewc = psizew*psizec; @@ -41819,10 +43866,10 @@ namespace cimg_library_suffixed { p1+=offy1; p2+=offy2; } return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + - patch_penalization*psizewc*psizeh*psized*occ(xc,yc,zc)/100); + patch_penalization*psizewc*psizeh*psized*penalty(xc,yc,zc)/100); } - static float _matchpatch(const CImg& img1, const CImg& img2, const CImg& occ, + static float _matchpatch(const CImg& img1, const CImg& img2, const CImg& penalty, const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec, const int x1, const int y1, const int x2, const int y2, @@ -41845,7 +43892,7 @@ namespace cimg_library_suffixed { p1+=offx1; p2+=offx2; } return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + - patch_penalization*psizewc*psizeh*occ(xc,yc)/100); + patch_penalization*psizewc*psizeh*penalty(xc,yc)/100); } //! Compute Euclidean distance function to a specified value. @@ -45043,7 +47090,7 @@ namespace cimg_library_suffixed { \param pattern An integer whose bits describe the line pattern. \param init_hatch Tells if a reinitialization of the hash state must be done. **/ - template + template CImg& draw_line(CImg& zbuffer, int x0, int y0, const float z0, int x1, int y1, const float z1, @@ -45147,11 +47194,10 @@ namespace cimg_library_suffixed { if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; - int - w1 = width() - 1, h1 = height() - 1, - dx01 = x1 - x0, dy01 = y1 - y0; - int - dtx01 = tx1 - tx0, dty01 = ty1 - ty0; + int w1 = width() - 1, h1 = height() - 1; + longT + dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0, + dtx01 = (longT)tx1 - tx0, dty01 = (longT)ty1 - ty0; const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); @@ -45165,15 +47211,17 @@ namespace cimg_library_suffixed { if (init_hatch) hatch = ~0U - (~0U>>1); cimg_init_scanline(opacity); - const int - step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, - hdy01tx = dy01*cimg::sign(dtx01)/2, hdy01ty = dy01*cimg::sign(dty01)/2, - cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + const longT + hdy01 = dy01*cimg::sign(dx01)/2, + hdy01tx = dy01*cimg::sign(dtx01)/2, + hdy01ty = dy01*cimg::sign(dty01)/2; + dy01+=dy01?0:1; for (int y = cy0; y!=cy1; y+=step) { - const int - yy0 = y - y0, + const longT + yy0 = (longT)y - y0, x = x0 + (dx01*yy0 + hdy01)/dy01, tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01, ty = ty0 + (dty01*yy0 + hdy01ty)/dy01; @@ -45227,9 +47275,8 @@ namespace cimg_library_suffixed { if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; float iz0 = 1/z0, iz1 = 1/z1; - int - w1 = width() - 1, h1 = height() - 1, - dx01 = x1 - x0, dy01 = y1 - y0; + int w1 = width() - 1, h1 = height() - 1; + longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0; float diz01 = iz1 - iz0, txz0 = tx0*iz0, txz1 = tx1*iz1, @@ -45248,14 +47295,14 @@ namespace cimg_library_suffixed { if (init_hatch) hatch = ~0U - (~0U>>1); cimg_init_scanline(opacity); - const int - step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, - cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + const longT hdy01 = dy01*cimg::sign(dx01)/2; + dy01+=dy01?0:1; for (int y = cy0; y!=cy1; y+=step) { - const int - yy0 = y - y0, + const longT + yy0 = (longT)y - y0, x = x0 + (dx01*yy0 + hdy01)/dy01; const float iz = iz0 + diz01*yy0/dy01, @@ -45322,9 +47369,8 @@ namespace cimg_library_suffixed { if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; float iz0 = 1/z0, iz1 = 1/z1; - int - w1 = width() - 1, h1 = height() - 1, - dx01 = x1 - x0, dy01 = y1 - y0; + int w1 = width() - 1, h1 = height() - 1; + longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0; float diz01 = iz1 - iz0, txz0 = tx0*iz0, txz1 = tx1*iz1, @@ -45343,14 +47389,14 @@ namespace cimg_library_suffixed { if (init_hatch) hatch = ~0U - (~0U>>1); cimg_init_scanline(opacity); - const int - step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, - cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + const longT hdy01 = dy01*cimg::sign(dx01)/2; + dy01+=dy01?0:1; for (int y = cy0; y!=cy1; y+=step) { - const int - yy0 = y - y0, + const longT + yy0 = (longT)y - y0, x = x0 + (dx01*yy0 + hdy01)/dy01; const float iz = iz0 + diz01*yy0/dy01, @@ -45715,17 +47761,19 @@ namespace cimg_library_suffixed { const int h1 = height() - 1, - dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, - dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), - cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1); + const longT + dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1, + dy01 = std::max((longT)1,(longT)y1 - y0), + dy02 = std::max((longT)1,(longT)y2 - y0), + dy12 = std::max((longT)1,(longT)y2 - y1), hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; - const float cbs = cimg::cut(brightness,0,2); cimg_init_scanline(opacity); for (int y = cy0; y<=cy2; ++y) { - const int yy0 = y - y0, yy1 = y - y1; - int + const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1; + longT xm = yxM) cimg::swap(xm,xM); @@ -45829,11 +47877,12 @@ namespace cimg_library_suffixed { if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2); if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; - const int - w1 = width() - 1, h1 = height() - 1, - dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, - dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), - cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1); + const longT + dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1, + dy01 = std::max((longT)1,(longT)y1 - y0), + dy02 = std::max((longT)1,(longT)y2 - y0), + dy12 = std::max((longT)1,(longT)y2 - y1), hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1; @@ -45841,8 +47890,8 @@ namespace cimg_library_suffixed { cimg_init_scanline(opacity); for (int y = cy0; y<=cy2; ++y) { - const int yy0 = y - y0, yy1 = y - y1; - int + const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1; + longT xm = yxM) cimg::swap(xm,xM,izm,izM); if (xM>=0 && xm<=w1) { const int - cxm = cimg::cut(xm,0,w1), - cxM = cimg::cut(xM,0,w1); + cxm = (int)cimg::cut(xm,(longT)0,(longT)w1), + cxM = (int)cimg::cut(xM,(longT)0,(longT)w1); T *ptrd = data(cxm,y); tz *ptrz = zbuffer.data(cxm,y); - const int dxmM = std::max(1,xM - xm); + const longT dxmM = std::max((longT)1,xM - xm); const float dizmM = izM - izm; for (int x = cxm; x<=cxM; ++x) { - const int xxm = x - xm; + const longT xxm = x - xm; const float iz = izm + dizmM*xxm/dxmM; if (iz>=*ptrz) { *ptrz = (tz)iz; @@ -45909,19 +47958,20 @@ namespace cimg_library_suffixed { if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2); if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; - const int - w1 = width() - 1, h1 = height() - 1, - dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, - dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), - cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1); + const longT + dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1, + dy01 = std::max((longT)1,(longT)y1 - y0), + dy02 = std::max((longT)1,(longT)y2 - y0), + dy12 = std::max((longT)1,(longT)y2 - y1), hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; cimg_init_scanline(opacity); for (int y = cy0; y<=cy2; ++y) { - const int yy0 = y - y0, yy1 = y - y1; - int + const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1; + longT xm = yxM) cimg::swap(xm,xM,bsm,bsM); if (xM>=0 && xm<=w1) { const int - cxm = cimg::cut(xm,0,w1), - cxM = cimg::cut(xM,0,w1); + cxm = (int)cimg::cut(xm,(longT)0,(longT)w1), + cxM = (int)cimg::cut(xM,(longT)0,(longT)w1); T *ptrd = data(cxm,y); - const int dxmM = std::max(1,xM - xm); + const longT dxmM = std::max((longT)1,xM - xm); const float dbsmM = bsM - bsm; for (int x = cxm; x<=cxM; ++x) { - const int xxm = x - xm; + const longT xxm = (longT)x - xm; const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); cimg_forC(*this,c) { const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; @@ -46042,17 +48092,64 @@ namespace cimg_library_suffixed { \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex. \param opacity Drawing opacity. **/ - template - CImg& draw_triangle(const int x0, const int y0, - const int x1, const int y1, - const int x2, const int y2, - const tc1 *const color1, - const tc2 *const color2, - const tc3 *const color3, + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *color0, + const tc *color1, + const tc *color2, const float opacity=1) { - const unsigned char one = 1; - cimg_forC(*this,c) - get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity); + typedef typename cimg::superset::type stc; + if (is_empty()) return *this; + if (!color0 || !color1 || !color2) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): One of the specified color is (null).", + cimg_instance); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,color0,color1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,color0,color2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,color1,color2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1); + const longT + dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1, + dy01 = std::max((longT)1,(longT)y1 - y0), + dy02 = std::max((longT)1,(longT)y2 - y0), + dy12 = std::max((longT)1,(longT)y2 - y1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + cimg_init_scanline(opacity); + + cimg_forC(*this,c) { + const stc dcolor01 = color1[c] - color0[c], dcolor02 = color2[c] - color0[c], dcolor12 = color2[c] - color1[c]; + + for (int y = cy0; y<=cy2; ++y) { + const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1; + longT + xm = yxM) cimg::swap(xm,xM,colorm,colorM); + if (xM>=0 && xm<=w1) { + const int + cxm = (int)cimg::cut(xm,(longT)0,(longT)w1), + cxM = (int)cimg::cut(xM,(longT)0,(longT)w1); + T *ptrd = data(cxm,y); + const longT dxmM = std::max((longT)1,xM - xm); + const stc dcolormM = colorM - colorm; + + for (int x = cxm; x<=cxM; ++x) { + const longT xxm = (longT)x - xm; + const stc col = colorm + dcolormM*xxm/dxmM; + ptrd[c*_sc_whd] = (T)(opacity>=1?col:col*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + ++ptrd; + } + } + } + } return *this; } @@ -47838,12 +49935,11 @@ namespace cimg_library_suffixed { CImg& draw_text(const int x0, const int y0, const char *const text, const tc1 *const foreground_color, const tc2 *const background_color, - const float opacity, const CImgList& font, ...) { - if (!font) return *this; + const float opacity, const CImgList* const font, ...) { + if (!font || !*font) return *this; CImg tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false); + std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,*font,false); } //! Draw a text string \overloading. @@ -47854,12 +49950,11 @@ namespace cimg_library_suffixed { CImg& draw_text(const int x0, const int y0, const char *const text, const tc *const foreground_color, const int, - const float opacity, const CImgList& font, ...) { - if (!font) return *this; + const float opacity, const CImgList* const font, ...) { + if (!font || !*font) return *this; CImg tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false); + std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,*font,false); } //! Draw a text string \overloading. @@ -47870,12 +49965,11 @@ namespace cimg_library_suffixed { CImg& draw_text(const int x0, const int y0, const char *const text, const int, const tc *const background_color, - const float opacity, const CImgList& font, ...) { - if (!font) return *this; + const float opacity, const CImgList* const font, ...) { + if (!font || !*font) return *this; CImg tmp(2048); - std::va_list ap; va_start(ap,font); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); - return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false); + std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,*font,false); } //! Draw a text string \overloading. @@ -47888,7 +49982,7 @@ namespace cimg_library_suffixed { \param background_color Array of spectrum() values of type \c T, defining the background color (0 means 'transparent'). \param opacity Drawing opacity. - \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise). + \param font_height Height of the text font (exact match for 13,32,64,128, interpolated otherwise). **/ template CImg& draw_text(const int x0, const int y0, @@ -47897,8 +49991,7 @@ namespace cimg_library_suffixed { const float opacity=1, const unsigned int font_height=13, ...) { if (!font_height) return *this; CImg tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); const CImgList& font = CImgList::font(font_height,true); _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true); return *this; @@ -47913,8 +50006,7 @@ namespace cimg_library_suffixed { if (!font_height) return *this; cimg::unused(background_color); CImg tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data); } @@ -47926,8 +50018,7 @@ namespace cimg_library_suffixed { const float opacity=1, const unsigned int font_height=13, ...) { if (!font_height) return *this; CImg tmp(2048); - std::va_list ap; va_start(ap,font_height); - cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data); } @@ -47937,12 +50028,7 @@ namespace cimg_library_suffixed { const tc1 *const foreground_color, const tc2 *const background_color, const float opacity, const CImgList& font, const bool is_native_font) { - if (!text) return *this; - if (!font) - throw CImgArgumentException(_cimg_instance - "draw_text(): Empty specified font.", - cimg_instance); - + if (!text || !font) return *this; const unsigned int text_length = (unsigned int)std::strlen(text); const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4; unsigned char o_ch, ch = 0; @@ -47957,13 +50043,20 @@ namespace cimg_library_suffixed { for (unsigned int i = 0; iw) w = x; x = 0; break; - case '\t' : x+=4*font[(int)' ']._width; break; - case ' ' : x+=font[(int)' ']._width; break; + case '\n' : + if (font._width>10) y+=font[10]._height; else y+=font[0]._height; + if (x>w) w = x; + x = 0; + break; + case '\t' : + if (font._width>32) x+=4*font[32]._width; else x+=4*font[0]._width; + break; + case ' ' : + if (font._width>32) x+=font[32]._width; else x+=font[0]._width; + break; default : if (ch'9')) || o_ch==';' || o_ch==':' || o_ch=='!') @@ -48010,9 +50103,18 @@ namespace cimg_library_suffixed { for (unsigned int i = 0; i10) y+=font[10]._height; else y+=font[0]._height; + x = x0; + break; + case '\t' : + case ' ' : { + const unsigned int + lw = (ch=='\t'?4:1)*font[font._width>32?32:0]._width, + lh = font[font._width>32?32:0]._height; + if (background_color) draw_rectangle(x,y,x + lw - 1,y + lh - 1,background_color,opacity); + x+=lw; + } break; default : if (ch letter = font[ch]; if (letter) { @@ -48023,7 +50125,7 @@ namespace cimg_library_suffixed { const unsigned int cmin = std::min(_spectrum,letter._spectrum); if (foreground_color) for (unsigned int c = 0; c& ref, const float tolerance2) const { const T *ptr1 = data(x,y,z), *ptr2 = ref._data; - const unsigned long off = _width*_height*_depth; + const ulongT off = _width*_height*_depth; float diff = 0; cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; } return diff<=tolerance2; @@ -48778,7 +50880,7 @@ namespace cimg_library_suffixed { \param y0 Y-coordinate of the upper-left pixel. \param x1 X-coordinate of the lower-right pixel. \param y1 Y-coordinate of the lower-right pixel. - \param colormap Colormap. + \param palette Colormap. \param opacity Drawing opacity. \param z0r Real part of the upper-left fractal vertex. \param z0i Imaginary part of the upper-left fractal vertex. @@ -49267,7 +51369,7 @@ namespace cimg_library_suffixed { if (colors._width>primitives._width) { static CImg default_light_texture; static const tc *lptr = 0; - static tc ref_values[64] = { 0 }; + static tc ref_values[64] = {}; const CImg& img = colors.back(); bool is_same_texture = (lptr==img._data); if (is_same_texture) @@ -51142,15 +53244,16 @@ namespace cimg_library_suffixed { Tint m0 = (Tint)cimg::type::max(), M0 = (Tint)cimg::type::min(); if (!normalization) { m0 = 0; M0 = 255; } else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; } - else + else { cimg_for(img2d,ptr,Tuchar) if (!cimg::type::is_inf(*ptr) && !cimg::type::is_nan(*ptr)) { if (*ptr<(Tuchar)m0) m0 = *ptr; if (*ptr>(Tuchar)M0) M0 = *ptr; } + } const T - val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0), - val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0); + val_minf = (T)(normalization==1 || normalization==3?m0 - cimg::abs(m0):m0), + val_pinf = (T)(normalization==1 || normalization==3?M0 + cimg::abs(M0):M0); if (is_nan) cimg_for(img2d,ptr,Tuchar) if (cimg::type::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values @@ -51314,25 +53417,29 @@ 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) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); - cimg_sprintf(message._data + std::strlen(message),")"); + unsigned int len = (unsigned int)std::strlen(message); + cimg_forC(*this,c) + cimg_snprintf(message._data + len,message._width - len,"%g ",(double)(*this)(x,0,0,c)); + len = std::strlen(message); + cimg_snprintf(message._data + len,message._width - len,")"); } 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); + ny1 = (unsigned int)(y0<=y1?y1:y0), + len = (unsigned int)std::strlen(message); const double cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32), cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32); if (y0>=0 && y1>=0) - cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", + cimg_snprintf(message._data + len,message._width - len," - Range ( %u:%g, %g ) - ( %u:%g, %g )", x0,cx0,cy0,x1 + one,cx1,cy1); else - cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", + cimg_snprintf(message._data + len,message._width - len," - 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); @@ -51517,9 +53624,11 @@ namespace cimg_library_suffixed { else if (!cimg::strcasecmp(ext,"tif") || !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename); - else if (!cimg::strcasecmp(ext,"cr2") || + else if (!cimg::strcasecmp(ext,"arw") || + !cimg::strcasecmp(ext,"cr2") || !cimg::strcasecmp(ext,"crw") || !cimg::strcasecmp(ext,"dcr") || + !cimg::strcasecmp(ext,"dng") || !cimg::strcasecmp(ext,"mrw") || !cimg::strcasecmp(ext,"nef") || !cimg::strcasecmp(ext,"orf") || @@ -51775,6 +53884,7 @@ namespace cimg_library_suffixed { "load_bmp(): Specified filename is (null).", cimg_instance); + const ulongT fsiz = file?(ulongT)cimg_max_buf_size:(ulongT)cimg::fsize(filename); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg header(54); cimg::fread(header._data,54,nfile); @@ -51811,6 +53921,13 @@ namespace cimg_library_suffixed { cimg_iobuffer = (ulongT)24*1024*1024, buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes); + if (buf_size>fsiz) + throw CImgIOException(_cimg_instance + "load_bmp(): File size %lu for filename '%s' does not match " + "encoded image dimensions (%d,%d).", + cimg_instance, + (long)fsiz,filename?filename:"(FILE*)",dx,dy); + CImg colormap; if (bpp<16) { if (!nb_colors) nb_colors = 1<& load_png(const char *const filename, unsigned int *const bits_per_pixel=0) { - return _load_png(0,filename,bits_per_pixel); + CImg& load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return _load_png(0,filename,bits_per_value); } //! Load image from a PNG file \newinstance. - static CImg get_load_png(const char *const filename, unsigned int *const bits_per_pixel=0) { - return CImg().load_png(filename,bits_per_pixel); + static CImg get_load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return CImg().load_png(filename,bits_per_value); } //! Load image from a PNG file \overloading. - CImg& load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) { - return _load_png(file,0,bits_per_pixel); + CImg& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return _load_png(file,0,bits_per_value); } //! Load image from a PNG file \newinstance. - static CImg get_load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) { - return CImg().load_png(file,bits_per_pixel); + static CImg get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return CImg().load_png(file,bits_per_value); } // (Note: Most of this function has been written by Eric Fausett) - CImg& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_pixel) { + CImg& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) { if (!file && !filename) throw CImgArgumentException(_cimg_instance "load_png(): Specified filename is (null).", cimg_instance); #ifndef cimg_use_png - cimg::unused(bits_per_pixel); + cimg::unused(bits_per_value); if (file) throw CImgIOException(_cimg_instance "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.", @@ -52190,7 +54307,7 @@ namespace cimg_library_suffixed { const char *nfilename = filename; std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb"); #endif - unsigned char pngCheck[8] = { 0 }; + unsigned char pngCheck[8] = {}; cimg::fread(pngCheck,8,(std::FILE*)nfile); if (png_sig_cmp(pngCheck,0,8)) { if (!file) cimg::fclose(nfile); @@ -52248,7 +54365,8 @@ namespace cimg_library_suffixed { int bit_depth, color_type, interlace_type; bool is_gray = false; png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0); - if (bits_per_pixel) *bits_per_pixel = (unsigned int)bit_depth; + png_set_interlace_handling(png_ptr); + if (bits_per_value) *bits_per_value = (unsigned int)bit_depth; // Transforms to unify image data if (color_type==PNG_COLOR_TYPE_PALETTE) { @@ -52768,6 +54886,7 @@ namespace cimg_library_suffixed { \param first_frame First frame to read (for multi-pages tiff). \param last_frame Last frame to read (for multi-pages tiff). \param step_frame Step value of frame reading. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. \param[out] voxel_size Voxel size, as stored in the filename. \param[out] description Description, as stored in the filename. \note @@ -52776,14 +54895,13 @@ namespace cimg_library_suffixed { - When libtiff is enabled, 2D and 3D (multipage) several channel per pixel are supported for char,uchar,short,ushort,float and \c double pixel types. - - If \c cimg_use_tif is not defined at compile time the + - If \c cimg_use_tiff is not defined at compile time the function uses CImg& load_other(const char*). **/ CImg& load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg *const description=0) { + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { if (!filename) throw CImgArgumentException(_cimg_instance "load_tiff(): Specified filename is (null).", @@ -52795,7 +54913,7 @@ namespace cimg_library_suffixed { unsigned int nlast_frame = first_frame1) throw CImgArgumentException(_cimg_instance "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.", @@ -52822,7 +54940,7 @@ namespace cimg_library_suffixed { TIFFSetDirectory(tif,0); CImg frame; for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { - frame._load_tiff(tif,l,voxel_size,description); + frame._load_tiff(tif,l,bits_per_value,voxel_size,description); if (l==nfirst_frame) assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum); if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum) @@ -52843,17 +54961,17 @@ namespace cimg_library_suffixed { //! Load image from a TIFF file \newinstance. static CImg get_load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg *const description=0) { - return CImg().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImg().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); } // (Original contribution by Jerome Boulanger). #ifdef cimg_use_tiff template - void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, - const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + void _load_tiff_tiled_contig(TIFF *const tif, const cimg_uint16 samplesperpixel, + const cimg_uint32 nx, const cimg_uint32 ny, + const cimg_uint32 tw, const cimg_uint32 th) { t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); if (buf) { for (unsigned int row = 0; row - void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, - const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + void _load_tiff_tiled_separate(TIFF *const tif, const cimg_uint16 samplesperpixel, + const cimg_uint32 nx, const cimg_uint32 ny, + const cimg_uint32 tw, const cimg_uint32 th) { t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); if (buf) { for (unsigned int vv = 0; vv - void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + void _load_tiff_contig(TIFF *const tif, const cimg_uint16 samplesperpixel, + const cimg_uint32 nx, const cimg_uint32 ny) { t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); if (buf) { - uint32 row, rowsperstrip = (uint32)-1; + cimg_uint32 row, rowsperstrip = (cimg_uint32)-1; TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); for (row = 0; rowny?ny - row:rowsperstrip); + cimg_uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, 0); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); @@ -52925,14 +55045,15 @@ namespace cimg_library_suffixed { } template - void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + void _load_tiff_separate(TIFF *const tif, const cimg_uint16 samplesperpixel, + const cimg_uint32 nx, const cimg_uint32 ny) { t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); if (buf) { - uint32 row, rowsperstrip = (uint32)-1; + cimg_uint32 row, rowsperstrip = (cimg_uint32)-1; TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); for (unsigned int vv = 0; vvny?ny - row:rowsperstrip); + cimg_uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, vv); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); @@ -52950,12 +55071,12 @@ namespace cimg_library_suffixed { } } - CImg& _load_tiff(TIFF *const tif, const unsigned int directory, + CImg& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value, float *const voxel_size, CImg *const description) { if (!TIFFSetDirectory(tif,directory)) return assign(); - uint16 samplesperpixel = 1, bitspersample = 8, photo = 0; - uint16 sampleformat = 1; - uint32 nx = 1, ny = 1; + cimg_uint16 samplesperpixel = 1, bitspersample = 8, photo = 0; + cimg_uint16 sampleformat = 1; + cimg_uint32 nx = 1, ny = 1; const char *const filename = TIFFFileName(tif); const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); @@ -52963,6 +55084,7 @@ namespace cimg_library_suffixed { TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); + if (bits_per_value) *bits_per_value = (unsigned int)bitspersample; if (voxel_size) { const char *s_description = 0; float vx = 0, vy = 0, vz = 0; @@ -52994,13 +55116,13 @@ namespace cimg_library_suffixed { (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)); + cimg_uint32 *const raster = (cimg_uint32*)_TIFFmalloc(nx*ny*sizeof(cimg_uint32)); if (!raster) { _TIFFfree(raster); TIFFClose(tif); throw CImgException(_cimg_instance "load_tiff(): Failed to allocate memory (%s) for file '%s'.", cimg_instance, - cimg::strbuffersize(nx*ny*sizeof(uint32)),filename); + cimg::strbuffersize(nx*ny*sizeof(cimg_uint32)),filename); } TIFFReadRGBAImage(tif,nx,ny,raster,0); switch (spectrum) { @@ -53010,9 +55132,9 @@ namespace cimg_library_suffixed { 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]); + (*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 : @@ -53026,10 +55148,10 @@ namespace cimg_library_suffixed { } _TIFFfree(raster); } else { // Other cases - uint16 config; + cimg_uint16 config; TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); if (TIFFIsTiled(tif)) { - uint32 tw = 1, th = 1; + cimg_uint32 tw = 1, th = 1; TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { @@ -53201,13 +55323,14 @@ namespace cimg_library_suffixed { if (!file) { CImg body(1024); const char *const ext = cimg::split_filename(filename,body); + const unsigned int len = (unsigned int)std::strlen(body); if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file nfile_header = cimg::fopen(filename,"rb"); - cimg_sprintf(body._data + std::strlen(body),".img"); + cimg_snprintf(body._data + len,body._width - len,".img"); nfile = cimg::fopen(body,"rb"); } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file nfile = cimg::fopen(filename,"rb"); - cimg_sprintf(body._data + std::strlen(body),".hdr"); + cimg_snprintf(body._data + len,body._width - len,".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 @@ -53464,12 +55587,12 @@ namespace cimg_library_suffixed { case 0 : break; case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; - std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough + std::strncpy(tmp1,tmp2,tmp1._width - 1); // Fallthrough case 1 : if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; - if (out[4]>=0) break; // fallthrough + if (out[4]>=0) break; // Fallthrough default : throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", pixel_type(), @@ -53612,6 +55735,13 @@ namespace cimg_library_suffixed { #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \ cimg::fread(dims,nbdim,nfile); \ if (endian) cimg::invert_endianness(dims,nbdim); \ + if ((ulongT)nwidth*nheight*ndepth*ndim>fsiz) \ + throw CImgIOException(_cimg_instance \ + "load_pandore(): File size %lu for filename '%s' does not match "\ + "encoded image dimensions (%d,%d,%d,%d).",\ + cimg_instance,\ + (long)fsiz,filename?filename:"(FILE*)",\ + (int)nwidth,(int)nheight,(int)ndepth,(int)ndim); \ assign(nwidth,nheight,ndepth,ndim); \ const size_t siz = size(); \ stype *buffer = new stype[siz]; \ @@ -53635,6 +55765,7 @@ namespace cimg_library_suffixed { "load_pandore(): Specified filename is (null).", cimg_instance); + const ulongT fsiz = file?(ulongT)cimg_max_buf_size:(ulongT)cimg::fsize(filename); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg header(32); cimg::fread(header._data,12,nfile); @@ -53645,8 +55776,8 @@ namespace cimg_library_suffixed { cimg_instance, filename?filename:"(FILE*)"); } - unsigned int imageid, dims[8] = { 0 }; - int ptbuf[4] = { 0 }; + unsigned int imageid, dims[8] = {}; + int ptbuf[4] = {}; cimg::fread(&imageid,1,nfile); const bool endian = imageid>255; if (endian) cimg::invert_endianness(imageid); @@ -53898,13 +56029,13 @@ namespace cimg_library_suffixed { "load_raw(): Cannot determine size of input file '%s'.", cimg_instance,filename?filename:"(FILE*)"); cimg::fseek(nfile,0,SEEK_END); - siz = cimg::ftell(nfile); + siz = (ulongT)cimg::ftell(nfile); if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; } else _size_y = (unsigned int)(siz*8); _size_x = _size_z = _size_c = 1; cimg::fseek(nfile,fpos,SEEK_SET); } - cimg::fseek(nfile,offset,SEEK_SET); + cimg::fseek(nfile,(longT)offset,SEEK_SET); assign(_size_x,_size_y,_size_z,_size_c,0); if (is_bool) { // Boolean data (bitwise) @@ -54339,13 +56470,26 @@ namespace cimg_library_suffixed { const CImg s_filename = CImg::string(filename)._system_strescape(); #if cimg_OS==1 if (!cimg::system("which gm")) { - cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-", - cimg::graphicsmagick_path(),s_filename.data()); + cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-", + cimg::graphicsmagick_path(), + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); file = popen(command,"r"); if (file) { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); - try { load_pnm(file); } catch (...) { + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { pclose(file); cimg::exception_mode(omode); throw CImgIOException(_cimg_instance @@ -54360,12 +56504,21 @@ namespace cimg_library_suffixed { } #endif do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"", - cimg::graphicsmagick_path(),s_filename.data(), + cimg::graphicsmagick_path(), + s_filename.data(), CImg::string(filename_tmp)._system_strescape().data()); cimg::system(command,cimg::graphicsmagick_path()); if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { @@ -54376,7 +56529,11 @@ namespace cimg_library_suffixed { filename); } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else load_pnm(filename_tmp); +#endif std::remove(filename_tmp); return *this; } @@ -54454,15 +56611,27 @@ namespace cimg_library_suffixed { const CImg s_filename = CImg::string(filename)._system_strescape(); #if cimg_OS==1 if (!cimg::system("which convert")) { - cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-", + cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-", cimg::imagemagick_path(), !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", - s_filename.data()); + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); file = popen(command,"r"); if (file) { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); - try { load_pnm(file); } catch (...) { + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { pclose(file); cimg::exception_mode(omode); throw CImgIOException(_cimg_instance @@ -54477,14 +56646,23 @@ namespace cimg_library_suffixed { } #endif do { - cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm", - cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); } while (file); cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"", cimg::imagemagick_path(), !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", - s_filename.data(),CImg::string(filename_tmp)._system_strescape().data()); + s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); cimg::system(command,cimg::imagemagick_path()); if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); @@ -54495,7 +56673,11 @@ namespace cimg_library_suffixed { filename); } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else load_pnm(filename_tmp); +#endif std::remove(filename_tmp); return *this; } @@ -54526,7 +56708,7 @@ namespace cimg_library_suffixed { cimg::medcon_path(), CImg::string(filename_tmp)._system_strescape().data(), CImg::string(filename)._system_strescape().data()); - cimg::system(command, cimg::medcon_path()); + cimg::system(command,cimg::medcon_path()); cimg::split_filename(filename_tmp,body); cimg_snprintf(command,command._width,"%s.hdr",body._data); @@ -54741,7 +56923,7 @@ namespace cimg_library_suffixed { "(no more than 100 cameras can be managed simultaneously).", cimg_instance, camera_index); - static cv::VideoCapture *captures[64] = { 0 }; + static cv::VideoCapture *captures[64] = {}; static unsigned int captures_w[64], captures_h[64]; if (release_camera) { cimg::mutex(9); @@ -54779,7 +56961,14 @@ namespace cimg_library_suffixed { for (unsigned int i = 0; igrab(); cv::Mat cvimg; captures[camera_index]->read(cvimg); - if (cvimg.empty()) assign(); else _cvmat2cimg(cvimg).move_to(*this); + if (cvimg.empty()) { + cimg::mutex(9,0); + load_camera(camera_index,0,0,0,true); // Release camera + throw CImgIOException(_cimg_instance + "load_camera(): Failed to retrieve a %ux%u frame from camera #%u.", + cimg_instance, + capture_width,capture_height,camera_index); + } else _cvmat2cimg(cvimg).move_to(*this); cimg::mutex(9,0); return *this; #else @@ -54941,7 +57130,7 @@ namespace cimg_library_suffixed { 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_singleclick) const { - unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0; + unsigned int oldw = 0, oldh = 0, _XYZ[3] = {}, key = 0; int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1, old_mouse_x = -1, old_mouse_y = -1; @@ -56200,7 +58389,7 @@ namespace cimg_library_suffixed { std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); CImg header(54,1,1,1,0); - unsigned char align_buf[4] = { 0 }; + unsigned char align_buf[4] = {}; const unsigned int align = (4 - (3*_width)%4)%4, buf_size = (3*_width + align)*height(), @@ -56418,7 +58607,7 @@ namespace cimg_library_suffixed { cimg::warn(_cimg_instance "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", cimg_instance, - filename,stmin,stmax); + stmin,stmax,filename); Magick::Image image(Magick::Geometry(_width,_height),"black"); image.type(Magick::TrueColorType); @@ -56518,7 +58707,7 @@ namespace cimg_library_suffixed { cimg::warn(_cimg_instance "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", cimg_instance, - filename,stmin,stmax); + stmin,stmax,filename); // Setup PNG structures for write png_voidp user_error_ptr = 0; @@ -57112,8 +59301,8 @@ namespace cimg_library_suffixed { /** \param filename Filename, as a C-string. \param compression_type Type of data compression. Can be { 0=None | 1=LZW | 2=JPEG }. - \param voxel_size Voxel size, to be stored in the filename. - \param description Description, to be stored in the filename. + \param[out] voxel_size Voxel size, to be stored in the filename. + \param[out] description Description, to be stored in the filename. \param use_bigtiff Allow to save big tiff files (>4Gb). \note - libtiff support is enabled by defining the precompilation @@ -57121,10 +59310,11 @@ namespace cimg_library_suffixed { - When libtiff is enabled, 2D and 3D (multipage) several channel per pixel are supported for char,uchar,short,ushort,float and \c double pixel types. - - If \c cimg_use_tif is not defined at compile time the + - If \c cimg_use_tiff is not defined at compile time the function uses CImg&save_other(const char*). **/ const CImg& save_tiff(const char *const filename, const unsigned int compression_type=0, + const float *const voxel_size=0, const char *const description=0, const bool use_bigtiff=true) const { if (!filename) @@ -57153,8 +59343,8 @@ namespace cimg_library_suffixed { #ifdef cimg_use_tiff -#define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \ - const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); } +#define _cimg_save_tiff(types,typed) if (!std::strcmp(types,pixel_type())) { \ + const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); } // [internal] Save a plane into a tiff file template @@ -57163,8 +59353,8 @@ namespace cimg_library_suffixed { const char *const description) const { if (is_empty() || !tif || pixel_t) return *this; const char *const filename = TIFFFileName(tif); - uint32 rowsperstrip = (uint32)-1; - uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric; + cimg_uint32 rowsperstrip = (cimg_uint32)-1; + cimg_uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric; if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; else photometric = PHOTOMETRIC_MINISBLACK; TIFFSetDirectory(tif,directory); @@ -57196,12 +59386,12 @@ namespace cimg_library_suffixed { rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); - TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); + TIFFSetField(tif,TIFFTAG_SOFTWARE,cimg_appname); t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); if (buf) { for (unsigned int row = 0; row<_height; row+=rowsperstrip) { - uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip); + cimg_uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif,row,0); tsize_t i = 0; for (unsigned int rr = 0; rr& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const unsigned int compression_type, const float *const voxel_size, const char *const description) const { - _cimg_save_tiff("bool",unsigned char,compression_type); - _cimg_save_tiff("unsigned char",unsigned char,compression_type); - _cimg_save_tiff("char",char,compression_type); - _cimg_save_tiff("unsigned short",unsigned short,compression_type); - _cimg_save_tiff("short",short,compression_type); - _cimg_save_tiff("unsigned int",unsigned int,compression_type); - _cimg_save_tiff("int",int,compression_type); - _cimg_save_tiff("unsigned int64",unsigned int,compression_type); - _cimg_save_tiff("int64",int,compression_type); - _cimg_save_tiff("float",float,compression_type); - _cimg_save_tiff("double",float,compression_type); + _cimg_save_tiff("uint8",cimg_uint8); + _cimg_save_tiff("int8",cimg_int8); + _cimg_save_tiff("uint16",cimg_uint16); + _cimg_save_tiff("int16",cimg_int16); + _cimg_save_tiff("uint32",cimg_uint32); + _cimg_save_tiff("int32",cimg_int32); + _cimg_save_tiff("uint64",cimg_uint32); // 'int64' as 'int32' + _cimg_save_tiff("int64",cimg_int32); + _cimg_save_tiff("float32",cimg_float32); + _cimg_save_tiff("float64",cimg_float32); // 'float64' as 'float32' const char *const filename = TIFFFileName(tif); throw CImgInstanceException(_cimg_instance "save_tiff(): Unsupported pixel type '%s' for file '%s'.", @@ -57307,12 +59496,12 @@ namespace cimg_library_suffixed { if (!cimg::strncasecmp(ext,"hdr",3)) { std::strcpy(hname,filename); std::strncpy(iname,filename,iname._width - 1); - cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); + cimg_snprintf(iname._data + std::strlen(iname) - 3,4,"img"); } if (!cimg::strncasecmp(ext,"img",3)) { std::strcpy(hname,filename); std::strncpy(iname,filename,iname._width - 1); - cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); + cimg_snprintf(hname._data + std::strlen(iname) - 3,4,"hdr"); } if (!cimg::strncasecmp(ext,"nii",3)) { std::strncpy(hname,filename,hname._width - 1); *iname = 0; @@ -57330,17 +59519,17 @@ namespace cimg_library_suffixed { ((short*)&(header[40]))[2] = (short)_height; ((short*)&(header[40]))[3] = (short)_depth; ((short*)&(header[40]))[4] = (short)_spectrum; - if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; - if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; - if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; - if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; - if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; - if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8; - if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; - if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; + if (!cimg::strcasecmp(pixel_type(),"bool") || + !cimg::strcasecmp(pixel_type(),"uint8") || + !cimg::strcasecmp(pixel_type(),"int8")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"uint16") || + !cimg::strcasecmp(pixel_type(),"int16")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"uint32") || + !cimg::strcasecmp(pixel_type(),"int32")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"uint64") || + !cimg::strcasecmp(pixel_type(),"int64")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"float32")) datatype = 16; + if (!cimg::strcasecmp(pixel_type(),"float64")) datatype = 64; if (datatype<0) throw CImgIOException(_cimg_instance "save_analyze(): Unsupported pixel type '%s' for file '%s'.", @@ -57458,28 +59647,28 @@ namespace cimg_library_suffixed { int inrpixsize = -1; const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; - if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { + if (!cimg::strcasecmp(pixel_type(),"uint8")) { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } - if (!cimg::strcasecmp(pixel_type(),"char")) { + if (!cimg::strcasecmp(pixel_type(),"int8")) { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } - if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { + if (!cimg::strcasecmp(pixel_type(),"uint16")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; } - if (!cimg::strcasecmp(pixel_type(),"short")) { + if (!cimg::strcasecmp(pixel_type(),"int16")) { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; } - if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { + if (!cimg::strcasecmp(pixel_type(),"uint32")) { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; } - if (!cimg::strcasecmp(pixel_type(),"int")) { + if (!cimg::strcasecmp(pixel_type(),"int32")) { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; } - if (!cimg::strcasecmp(pixel_type(),"float")) { + if (!cimg::strcasecmp(pixel_type(),"float32")) { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; } - if (!cimg::strcasecmp(pixel_type(),"double")) { + if (!cimg::strcasecmp(pixel_type(),"float64")) { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; } if (inrpixsize<=0) @@ -57492,9 +59681,11 @@ namespace cimg_library_suffixed { CImg header(257); int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", _width,_height,_depth,_spectrum); - if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n", - voxel_size[0],voxel_size[1],voxel_size[2]); - err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); + if (voxel_size) + err+=cimg_snprintf(header._data + err,128,"VX=%g\nVY=%g\nVZ=%g\n", + voxel_size[0],voxel_size[1],voxel_size[2]); + err+=cimg_snprintf(header._data + err,128,"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); @@ -57685,95 +59876,95 @@ namespace cimg_library_suffixed { 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 }; + unsigned int nbdims, dims[5] = {}; bool saved = false; - _cimg_save_pandore_case(1,1,1,"unsigned char",2); - _cimg_save_pandore_case(1,1,1,"char",3); - _cimg_save_pandore_case(1,1,1,"unsigned short",3); - _cimg_save_pandore_case(1,1,1,"short",3); - _cimg_save_pandore_case(1,1,1,"unsigned int",3); - _cimg_save_pandore_case(1,1,1,"int",3); - _cimg_save_pandore_case(1,1,1,"unsigned int64",3); + _cimg_save_pandore_case(1,1,1,"uint8",2); + _cimg_save_pandore_case(1,1,1,"int8",3); + _cimg_save_pandore_case(1,1,1,"uint16",3); + _cimg_save_pandore_case(1,1,1,"int16",3); + _cimg_save_pandore_case(1,1,1,"uint32",3); + _cimg_save_pandore_case(1,1,1,"int32",3); + _cimg_save_pandore_case(1,1,1,"uint64",3); _cimg_save_pandore_case(1,1,1,"int64",3); - _cimg_save_pandore_case(1,1,1,"float",4); - _cimg_save_pandore_case(1,1,1,"double",4); + _cimg_save_pandore_case(1,1,1,"float32",4); + _cimg_save_pandore_case(1,1,1,"float64",4); - _cimg_save_pandore_case(0,1,1,"unsigned char",5); - _cimg_save_pandore_case(0,1,1,"char",6); - _cimg_save_pandore_case(0,1,1,"unsigned short",6); - _cimg_save_pandore_case(0,1,1,"short",6); - _cimg_save_pandore_case(0,1,1,"unsigned int",6); - _cimg_save_pandore_case(0,1,1,"int",6); - _cimg_save_pandore_case(0,1,1,"unsigned int64",6); + _cimg_save_pandore_case(0,1,1,"uint8",5); + _cimg_save_pandore_case(0,1,1,"int8",6); + _cimg_save_pandore_case(0,1,1,"uint16",6); + _cimg_save_pandore_case(0,1,1,"int16",6); + _cimg_save_pandore_case(0,1,1,"uint32",6); + _cimg_save_pandore_case(0,1,1,"int32",6); + _cimg_save_pandore_case(0,1,1,"uint64",6); _cimg_save_pandore_case(0,1,1,"int64",6); - _cimg_save_pandore_case(0,1,1,"float",7); - _cimg_save_pandore_case(0,1,1,"double",7); + _cimg_save_pandore_case(0,1,1,"float32",7); + _cimg_save_pandore_case(0,1,1,"float64",7); - _cimg_save_pandore_case(0,0,1,"unsigned char",8); - _cimg_save_pandore_case(0,0,1,"char",9); - _cimg_save_pandore_case(0,0,1,"unsigned short",9); - _cimg_save_pandore_case(0,0,1,"short",9); - _cimg_save_pandore_case(0,0,1,"unsigned int",9); - _cimg_save_pandore_case(0,0,1,"int",9); - _cimg_save_pandore_case(0,0,1,"unsigned int64",9); + _cimg_save_pandore_case(0,0,1,"uint8",8); + _cimg_save_pandore_case(0,0,1,"int8",9); + _cimg_save_pandore_case(0,0,1,"uint16",9); + _cimg_save_pandore_case(0,0,1,"int16",9); + _cimg_save_pandore_case(0,0,1,"uint32",9); + _cimg_save_pandore_case(0,0,1,"int32",9); + _cimg_save_pandore_case(0,0,1,"uint64",9); _cimg_save_pandore_case(0,0,1,"int64",9); - _cimg_save_pandore_case(0,0,1,"float",10); - _cimg_save_pandore_case(0,0,1,"double",10); + _cimg_save_pandore_case(0,0,1,"float32",10); + _cimg_save_pandore_case(0,0,1,"float64",10); - _cimg_save_pandore_case(0,1,3,"unsigned char",16); - _cimg_save_pandore_case(0,1,3,"char",17); - _cimg_save_pandore_case(0,1,3,"unsigned short",17); - _cimg_save_pandore_case(0,1,3,"short",17); - _cimg_save_pandore_case(0,1,3,"unsigned int",17); - _cimg_save_pandore_case(0,1,3,"int",17); - _cimg_save_pandore_case(0,1,3,"unsigned int64",17); + _cimg_save_pandore_case(0,1,3,"uint8",16); + _cimg_save_pandore_case(0,1,3,"int8",17); + _cimg_save_pandore_case(0,1,3,"uint16",17); + _cimg_save_pandore_case(0,1,3,"int16",17); + _cimg_save_pandore_case(0,1,3,"uint32",17); + _cimg_save_pandore_case(0,1,3,"int32",17); + _cimg_save_pandore_case(0,1,3,"uint64",17); _cimg_save_pandore_case(0,1,3,"int64",17); - _cimg_save_pandore_case(0,1,3,"float",18); - _cimg_save_pandore_case(0,1,3,"double",18); + _cimg_save_pandore_case(0,1,3,"float32",18); + _cimg_save_pandore_case(0,1,3,"float64",18); - _cimg_save_pandore_case(0,0,3,"unsigned char",19); - _cimg_save_pandore_case(0,0,3,"char",20); - _cimg_save_pandore_case(0,0,3,"unsigned short",20); - _cimg_save_pandore_case(0,0,3,"short",20); - _cimg_save_pandore_case(0,0,3,"unsigned int",20); - _cimg_save_pandore_case(0,0,3,"int",20); - _cimg_save_pandore_case(0,0,3,"unsigned int64",20); + _cimg_save_pandore_case(0,0,3,"uint8",19); + _cimg_save_pandore_case(0,0,3,"int8",20); + _cimg_save_pandore_case(0,0,3,"uint16",20); + _cimg_save_pandore_case(0,0,3,"int16",20); + _cimg_save_pandore_case(0,0,3,"uint32",20); + _cimg_save_pandore_case(0,0,3,"int32",20); + _cimg_save_pandore_case(0,0,3,"uint64",20); _cimg_save_pandore_case(0,0,3,"int64",20); - _cimg_save_pandore_case(0,0,3,"float",21); - _cimg_save_pandore_case(0,0,3,"double",21); + _cimg_save_pandore_case(0,0,3,"float32",21); + _cimg_save_pandore_case(0,0,3,"float64",21); - _cimg_save_pandore_case(1,1,0,"unsigned char",22); - _cimg_save_pandore_case(1,1,0,"char",23); - _cimg_save_pandore_case(1,1,0,"unsigned short",23); - _cimg_save_pandore_case(1,1,0,"short",23); - _cimg_save_pandore_case(1,1,0,"unsigned int",23); - _cimg_save_pandore_case(1,1,0,"int",23); - _cimg_save_pandore_case(1,1,0,"unsigned int64",23); + _cimg_save_pandore_case(1,1,0,"uint8",22); + _cimg_save_pandore_case(1,1,0,"int8",23); + _cimg_save_pandore_case(1,1,0,"uint16",23); + _cimg_save_pandore_case(1,1,0,"int16",23); + _cimg_save_pandore_case(1,1,0,"uint32",23); + _cimg_save_pandore_case(1,1,0,"int32",23); + _cimg_save_pandore_case(1,1,0,"uint64",23); _cimg_save_pandore_case(1,1,0,"int64",23); - _cimg_save_pandore_case(1,1,0,"float",25); - _cimg_save_pandore_case(1,1,0,"double",25); + _cimg_save_pandore_case(1,1,0,"float32",25); + _cimg_save_pandore_case(1,1,0,"float64",25); - _cimg_save_pandore_case(0,1,0,"unsigned char",26); - _cimg_save_pandore_case(0,1,0,"char",27); - _cimg_save_pandore_case(0,1,0,"unsigned short",27); - _cimg_save_pandore_case(0,1,0,"short",27); - _cimg_save_pandore_case(0,1,0,"unsigned int",27); - _cimg_save_pandore_case(0,1,0,"int",27); - _cimg_save_pandore_case(0,1,0,"unsigned int64",27); + _cimg_save_pandore_case(0,1,0,"uint8",26); + _cimg_save_pandore_case(0,1,0,"int8",27); + _cimg_save_pandore_case(0,1,0,"uint16",27); + _cimg_save_pandore_case(0,1,0,"int16",27); + _cimg_save_pandore_case(0,1,0,"uint32",27); + _cimg_save_pandore_case(0,1,0,"int32",27); + _cimg_save_pandore_case(0,1,0,"uint64",27); _cimg_save_pandore_case(0,1,0,"int64",27); - _cimg_save_pandore_case(0,1,0,"float",29); - _cimg_save_pandore_case(0,1,0,"double",29); + _cimg_save_pandore_case(0,1,0,"float32",29); + _cimg_save_pandore_case(0,1,0,"float64",29); - _cimg_save_pandore_case(0,0,0,"unsigned char",30); - _cimg_save_pandore_case(0,0,0,"char",31); - _cimg_save_pandore_case(0,0,0,"unsigned short",31); - _cimg_save_pandore_case(0,0,0,"short",31); - _cimg_save_pandore_case(0,0,0,"unsigned int",31); - _cimg_save_pandore_case(0,0,0,"int",31); - _cimg_save_pandore_case(0,0,0,"unsigned int64",31); + _cimg_save_pandore_case(0,0,0,"uint8",30); + _cimg_save_pandore_case(0,0,0,"int8",31); + _cimg_save_pandore_case(0,0,0,"uint16",31); + _cimg_save_pandore_case(0,0,0,"int16",31); + _cimg_save_pandore_case(0,0,0,"uint32",31); + _cimg_save_pandore_case(0,0,0,"int32",31); + _cimg_save_pandore_case(0,0,0,"uint64",31); _cimg_save_pandore_case(0,0,0,"int64",31); - _cimg_save_pandore_case(0,0,0,"float",33); - _cimg_save_pandore_case(0,0,0,"double",33); + _cimg_save_pandore_case(0,0,0,"float32",33); + _cimg_save_pandore_case(0,0,0,"float64",33); if (!file) cimg::fclose(nfile); return *this; @@ -58081,7 +60272,7 @@ namespace cimg_library_suffixed { cimg::gzip_path(), CImg::string(filename_tmp)._system_strescape().data(), CImg::string(filename)._system_strescape().data()); - cimg::system(command, cimg::gzip_path()); + cimg::system(command,cimg::gzip_path()); file = cimg::std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimg_instance @@ -58139,7 +60330,7 @@ namespace cimg_library_suffixed { cimg::graphicsmagick_path(),quality, CImg::string(filename_tmp)._system_strescape().data(), CImg::string(filename)._system_strescape().data()); - cimg::system(command, cimg::graphicsmagick_path()); + cimg::system(command,cimg::graphicsmagick_path()); file = cimg::std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimg_instance @@ -58194,7 +60385,7 @@ namespace cimg_library_suffixed { cimg::imagemagick_path(),quality, CImg::string(filename_tmp)._system_strescape().data(), CImg::string(filename)._system_strescape().data()); - cimg::system(command, cimg::imagemagick_path()); + cimg::system(command,cimg::imagemagick_path()); file = cimg::std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimg_instance @@ -58233,7 +60424,7 @@ namespace cimg_library_suffixed { cimg::medcon_path(), CImg::string(filename)._system_strescape().data(), CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command, cimg::medcon_path()); + cimg::system(command,cimg::medcon_path()); std::remove(filename_tmp); cimg::split_filename(filename_tmp,body); cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data); @@ -58308,9 +60499,10 @@ namespace cimg_library_suffixed { /** \param is_compressed tells if zlib compression must be used for serialization (this requires 'cimg_use_zlib' been enabled). + \param header_size Reserve empty bytes as a starting header. **/ - CImg get_serialize(const bool is_compressed=false) const { - return CImgList(*this,true).get_serialize(is_compressed); + CImg get_serialize(const bool is_compressed=false, const unsigned int header_size=0) const { + return CImgList(*this,true).get_serialize(is_compressed,header_size); } // [internal] Return a 40x38 color logo of a 'danger' item. @@ -59245,7 +61437,7 @@ namespace cimg_library_suffixed { Return a \c char* string containing the usual type name of the image pixel values (i.e. a stringified version of the template parameter \c T). \note - - The returned string may contain spaces (as in \c "unsigned char"). + - The returned string does not contain any spaces. - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. **/ static const char* pixel_type() { @@ -61038,7 +63230,9 @@ namespace cimg_library_suffixed { is_loaded = true; try { if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); - else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else if (!cimg::strcasecmp(f_type,"tif") && + cimg::strcasecmp(ext,"nef") && + cimg::strcasecmp(ext,"dng")) load_tiff(filename); else is_loaded = false; } catch (CImgIOException&) { is_loaded = false; } } @@ -61095,17 +63289,17 @@ namespace cimg_library_suffixed { #ifdef cimg_use_zlib #define _cimgz_load_cimg_case(Tss) { \ Bytef *const cbuf = new Bytef[csiz]; \ - cimg::fread(cbuf,csiz,nfile); \ + cimg::fread(cbuf,(size_t)csiz,nfile); \ if (is_bool) { \ CImg raw(W*H*D*C/8); \ - uLongf destlen = (ulongT)raw.size(); \ - uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ + uLongf destlen = (uLongf)raw.size(); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ img.assign(W,H,D,C); \ img._uchar2bool(raw,raw.size(),false); \ } else { \ CImg raw(W,H,D,C); \ - uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \ - uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \ + uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ raw.move_to(img); \ } \ @@ -61119,13 +63313,15 @@ namespace cimg_library_suffixed { filename?filename:"(FILE*)"); #endif -#define _cimg_load_cimg_case(Ts,Tss) \ - if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ +#define _cimg_load_cimg_case(Ts1,Ts2,Ts3,Tss) \ + if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \ + (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \ + (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \ const bool is_bool = cimg::type::string()==cimg::type::string(); \ 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 = cimg_sscanf(tmp,"%u %u %u %u #%lu",&W,&H,&D,&C,&csiz))<4) \ + if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&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, \ @@ -61141,7 +63337,7 @@ namespace cimg_library_suffixed { for (ulongT to_read = img.size(); to_read; ) { \ raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ cimg::fread(raw._data,raw._width,nfile); \ - CImg(ptrd,std::min((ulongT)8*raw._width,(ulongT)(img.end() - ptrd)),1,1,1,true).\ + CImg(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\ _uchar2bool(raw,raw._width,false); \ to_read-=raw._width; \ } \ @@ -61173,41 +63369,37 @@ namespace cimg_library_suffixed { CImg tmp(256), str_pixeltype(256), str_endian(256); *tmp = *str_pixeltype = *str_endian = 0; unsigned int j, N = 0, W, H, D, C; - unsigned long csiz; + cimg_uint64 csiz; int i, err; do { j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; } while (*tmp=='#' && i>=0); - err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]", &N,str_pixeltype._data,str_endian._data); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimglist_instance - "load_cimg(): CImg header not found in file '%s'.", + "load_cimg(): File or CImg header not found in file '%s'.", cimglist_instance, filename?filename:"(FILE*)"); } if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; assign(N); - _cimg_load_cimg_case("bool",bool); - _cimg_load_cimg_case("unsigned_char",unsigned char); - _cimg_load_cimg_case("uchar",unsigned char); - _cimg_load_cimg_case("char",char); - _cimg_load_cimg_case("unsigned_short",unsigned short); - _cimg_load_cimg_case("ushort",unsigned short); - _cimg_load_cimg_case("short",short); - _cimg_load_cimg_case("unsigned_int",unsigned int); - _cimg_load_cimg_case("uint",unsigned int); - _cimg_load_cimg_case("int",int); - _cimg_load_cimg_case("unsigned_long",ulongT); - _cimg_load_cimg_case("ulong",ulongT); - _cimg_load_cimg_case("long",longT); - _cimg_load_cimg_case("unsigned_int64",uint64T); - _cimg_load_cimg_case("uint64",uint64T); - _cimg_load_cimg_case("int64",int64T); - _cimg_load_cimg_case("float",float); - _cimg_load_cimg_case("double",double); + _cimg_load_cimg_case("bool",0,0,cimg_uint8); + _cimg_load_cimg_case("uint8","unsigned_char","uchar",cimg_uint8); + _cimg_load_cimg_case("int8",0,0,cimg_int8); + _cimg_load_cimg_case("char",0,0,char); + _cimg_load_cimg_case("uint16","unsigned_short","ushort",cimg_uint16); + _cimg_load_cimg_case("int16","short",0,cimg_int16); + _cimg_load_cimg_case("uint32","unsigned_int","uint",cimg_uint32); + _cimg_load_cimg_case("int32","int",0,cimg_int32); + _cimg_load_cimg_case("unsigned_long","ulong",0,cimg_ulong); + _cimg_load_cimg_case("long",0,0,cimg_long); + _cimg_load_cimg_case("uint64","unsigned_int64",0,cimg_uint64); + _cimg_load_cimg_case("int64",0,0,cimg_int64); + _cimg_load_cimg_case("float32","float",0,cimg_float32); + _cimg_load_cimg_case("float64","double",0,cimg_float64); if (!loaded) { if (!file) cimg::fclose(nfile); @@ -61279,8 +63471,10 @@ namespace cimg_library_suffixed { const unsigned int z0, const unsigned int c0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) { -#define _cimg_load_cimg_case2(Ts,Tss) \ - if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ +#define _cimg_load_cimg_case2(Ts1,Ts2,Ts3,Tss) \ + if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \ + (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \ + (Ts3 && !cimg::strcasecmp(Ts3,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; \ @@ -61358,7 +63552,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 = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]", &N,str_pixeltype._data,str_endian._data); if (err<2) { if (!file) cimg::fclose(nfile); @@ -61377,24 +63571,20 @@ namespace cimg_library_suffixed { cimglist_instance, n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N); assign(1 + nn1 - n0); - _cimg_load_cimg_case2("bool",bool); - _cimg_load_cimg_case2("unsigned_char",unsigned char); - _cimg_load_cimg_case2("uchar",unsigned char); - _cimg_load_cimg_case2("char",char); - _cimg_load_cimg_case2("unsigned_short",unsigned short); - _cimg_load_cimg_case2("ushort",unsigned short); - _cimg_load_cimg_case2("short",short); - _cimg_load_cimg_case2("unsigned_int",unsigned int); - _cimg_load_cimg_case2("uint",unsigned int); - _cimg_load_cimg_case2("int",int); - _cimg_load_cimg_case2("unsigned_long",ulongT); - _cimg_load_cimg_case2("ulong",ulongT); - _cimg_load_cimg_case2("long",longT); - _cimg_load_cimg_case2("unsigned_int64",uint64T); - _cimg_load_cimg_case2("uint64",uint64T); - _cimg_load_cimg_case2("int64",int64T); - _cimg_load_cimg_case2("float",float); - _cimg_load_cimg_case2("double",double); + _cimg_load_cimg_case2("bool",0,0,cimg_uint8); + _cimg_load_cimg_case2("uint8","unsigned char","uchar",cimg_uint8); + _cimg_load_cimg_case2("int8",0,0,cimg_int8); + _cimg_load_cimg_case2("char",0,0,char); + _cimg_load_cimg_case2("uint16","unsigned_short","ushort",cimg_uint16); + _cimg_load_cimg_case2("int16","short",0,cimg_int16); + _cimg_load_cimg_case2("uint32","unsigned_int","uint",cimg_uint32); + _cimg_load_cimg_case2("int32","int",0,cimg_int32); + _cimg_load_cimg_case2("unsigned_long","ulong",0,cimg_ulong); + _cimg_load_cimg_case2("long",0,0,cimg_long); + _cimg_load_cimg_case2("uint64","unsigned_int64",0,cimg_uint64); + _cimg_load_cimg_case2("int64",0,0,cimg_int64); + _cimg_load_cimg_case2("float32","float",0,cimg_float32); + _cimg_load_cimg_case2("float64","double",0,cimg_float64); if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimglist_instance @@ -61584,7 +63774,7 @@ namespace cimg_library_suffixed { cimglist_instance); if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) throw CImgArgumentException(_cimglist_instance - "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.", + "load_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.", cimglist_instance, chroma_subsampling,filename?filename:"(FILE*)"); const unsigned int @@ -61707,7 +63897,7 @@ namespace cimg_library_suffixed { cimglist_instance,filename); return load_ffmpeg_external(filename); #else - static cv::VideoCapture *captures[32] = { 0 }; + static cv::VideoCapture *captures[32] = {}; static CImgList filenames(32); static CImg positions(32,1,1,1,0); static int last_used_index = -1; @@ -61861,7 +64051,7 @@ namespace cimg_library_suffixed { cimg::ffmpeg_path(), CImg::string(filename)._system_strescape().data(), CImg::string(filename_tmp2)._system_strescape().data()); - cimg::system(command, cimg::ffmpeg_path()); + cimg::system(command,cimg::ffmpeg_path()); const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); assign(); @@ -61925,7 +64115,7 @@ namespace cimg_library_suffixed { cimg::imagemagick_path(), CImg::string(filename)._system_strescape().data(), CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command, cimg::imagemagick_path()); + cimg::system(command,cimg::imagemagick_path()); const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); assign(); @@ -61988,7 +64178,7 @@ namespace cimg_library_suffixed { cimg::gunzip_path(), CImg::string(filename)._system_strescape().data(), CImg::string(filename_tmp)._system_strescape().data()); - cimg::system(command, cimg::gunzip_path()); + cimg::system(command,cimg::gunzip_path()); if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException(_cimglist_instance @@ -62013,20 +64203,20 @@ namespace cimg_library_suffixed { \param first_frame Index of first image frame to read. \param last_frame Index of last image frame to read. \param step_frame Step applied between each frame. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. \param[out] voxel_size Voxel size, as stored in the filename. \param[out] description Description, as stored in the filename. **/ CImgList& load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg *const description=0) { + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { const unsigned int nfirst_frame = first_frame=nb_images) nlast_frame = nb_images - 1; assign(1 + (nlast_frame - nfirst_frame)/nstep_frame); TIFFSetDirectory(tif,0); - cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,voxel_size,description); + cimglist_for(*this,l) + _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description); TIFFClose(tif); } else throw CImgIOException(_cimglist_instance "load_tiff(): Failed to open file '%s'.", @@ -62067,10 +64258,9 @@ namespace cimg_library_suffixed { //! Load a multi-page TIFF file \newinstance. static CImgList get_load_tiff(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, - const unsigned int step_frame=1, - float *const voxel_size=0, - CImg *const description=0) { - return CImgList().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description); + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImgList().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); } //@} @@ -62102,7 +64292,7 @@ namespace cimg_library_suffixed { if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1)); else std::fprintf(cimg::output(),".\n"); - char tmp[16] = { 0 }; + char tmp[16] = {}; cimglist_for(*this,ll) { cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll); std::fprintf(cimg::output()," "); @@ -62414,7 +64604,6 @@ namespace cimg_library_suffixed { #else #define _cimg_save_gif_extension "ppm" #endif - do { cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); @@ -62424,11 +64613,18 @@ namespace cimg_library_suffixed { cimglist_for(*this,l) { cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1); CImg::string(filename_tmp2).move_to(filenames); - if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2); - else _data[l].save(filename_tmp2); + CImg frame; + if (_data[l]._depth>1) _data[l].get_slice(0).move_to(frame); else frame.assign(_data[l],true); + if (frame._spectrum>4) frame.assign(frame.get_channels(0,3),false); + else if (frame._spectrum==1) frame.assign(frame.get_resize(-100,-100,1,3),false); + else if (frame._spectrum==2) + frame.assign(frame.get_resize(-100,-100,1,4).draw_image(0,0,0,2,frame.get_shared_channel(0)),false); + frame.save(filename_tmp2); } - cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u", - cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops); + cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u -dispose previous", + cimg::imagemagick_path(), + (unsigned int)std::max(0.f,cimg::round(100/fps)), + nb_loops); CImg::string(command).move_to(filenames,0); cimg_snprintf(command,command._width,"\"%s\"", CImg::string(filename)._system_strescape().data()); @@ -62437,7 +64633,7 @@ namespace cimg_library_suffixed { cimg_for(_command,p,char) if (!*p) *p = ' '; _command.back() = 0; - cimg::system(_command, cimg::imagemagick_path()); + cimg::system(_command,cimg::imagemagick_path()); file = cimg::std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimglist_instance @@ -62544,10 +64740,7 @@ namespace cimg_library_suffixed { const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const bool is_bool = ptype==cimg::type::string(); - if (!is_bool && std::strstr(ptype,"unsigned")==ptype) - std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); - else - std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype); + std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype); cimglist_for(*this,l) { const CImg& img = _data[l]; @@ -62619,8 +64812,10 @@ namespace cimg_library_suffixed { const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0) const { -#define _cimg_save_cimg_case(Ts,Tss) \ - if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ +#define _cimg_save_cimg_case(Ts1,Ts2,Ts3,Tss) \ + if (!saved && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \ + (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \ + (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \ for (unsigned int l = 0; l::string(filename_tmp)._system_strescape().data(), CImg::string(filename)._system_strescape().data()); - cimg::system(command, cimg::gzip_path()); + cimg::system(command,cimg::gzip_path()); file = cimg::std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimglist_instance @@ -62900,7 +65093,7 @@ namespace cimg_library_suffixed { CImg nfilename(1024); cimglist_for(*this,l) { cimg::number_filename(body,l,6,nfilename); - if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); + if (*ext) cimg_snprintf(nfilename._data + std::strlen(nfilename),64,".%s",ext); _data[l].save_gzip_external(nfilename); } } @@ -62922,7 +65115,7 @@ namespace cimg_library_suffixed { return save_ffmpeg_external(filename,fps); #else try { - static cv::VideoWriter *writers[32] = { 0 }; + static cv::VideoWriter *writers[32] = {}; static CImgList filenames(32); static CImg sizes(32,2,1,1,0); static int last_used_index = -1; @@ -62993,18 +65186,21 @@ namespace cimg_library_suffixed { cimg::warn(_cimglist_instance "save_video(): Skip empty frame %d for file '%s'.", cimglist_instance,l,filename); - if (src._depth>1 || src._spectrum>3) + if (src._spectrum>3) cimg::warn(_cimglist_instance "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). " "Some image data may be ignored when writing frame into video file '%s'.", cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename); - if (src._width==W && src._height==H && src._spectrum==3) - writers[index]->write(CImg(src)._cimg2cvmat()); - else { - CImg _src(src,false); - _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H); - _src.resize(W,H,1,3,_src._spectrum==1); - writers[index]->write(_src._cimg2cvmat()); + cimg_forZ(src,z) { + CImg _src = src._depth>1?src.get_slice(z):src.get_shared(); + if (_src._width==W && _src._height==H && _src._spectrum==3) + writers[index]->write(CImg(_src)._cimg2cvmat()); + else { + CImg __src(_src,false); + __src.channels(0,std::min(__src._spectrum - 1,2U)).resize(W,H); + __src.resize(W,H,1,3,__src._spectrum==1); + writers[index]->write(__src._cimg2cvmat()); + } } } cimg::mutex(9,0); @@ -63062,23 +65258,28 @@ namespace cimg_library_suffixed { cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); } while (file); + unsigned int frame = 1; cimglist_for(*this,l) { - cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1); - CImg::string(filename_tmp2).move_to(filenames); - CImg tmp = _data[l].get_shared(); - if (tmp._width%2 || tmp._height%2) // Force output to have an even number of columns and rows - tmp.assign(tmp.get_resize(tmp._width + (tmp._width%2),tmp._height + (tmp._height%2),1,-100,0),false); - if (tmp._depth>1 || tmp._spectrum!=3) // Force output to be one slice, in color - tmp.assign(tmp.get_resize(-100,-100,1,3),false); - tmp.save_pnm(filename_tmp2); + CImg& src = _data[l]; + cimg_forZ(src,z) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,frame); + CImg::string(filename_tmp2).move_to(filenames); + CImg _src = src._depth>1?src.get_slice(z):src.get_shared(); + if (_src._width%2 || _src._height%2) // Force output to have an even number of columns and rows + _src.assign(_src.get_resize(_src._width + (_src._width%2),_src._height + (_src._height%2),1,-100,0),false); + if (_src._spectrum!=3) // Force output to be one slice, in color + _src.assign(_src.get_resize(-100,-100,1,3),false); + _src.save_pnm(filename_tmp2); + ++frame; + } } cimg_snprintf(command,command._width, - "\"%s\" -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"", + "\"%s\" -framerate %u -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"", cimg::ffmpeg_path(), - CImg::string(filename_tmp)._system_strescape().data(), + fps,CImg::string(filename_tmp)._system_strescape().data(), _codec,bitrate,fps, CImg::string(filename)._system_strescape().data()); - cimg::system(command, cimg::ffmpeg_path()); + cimg::system(command,cimg::ffmpeg_path()); file = cimg::std_fopen(filename,"rb"); if (!file) throw CImgIOException(_cimglist_instance @@ -63094,8 +65295,9 @@ namespace cimg_library_suffixed { /** \param is_compressed tells if zlib compression must be used for serialization (this requires 'cimg_use_zlib' been enabled). + \param header_size Reserve empty bytes as a starting header. **/ - CImg get_serialize(const bool is_compressed=false) const { + CImg get_serialize(const bool is_compressed=false, const unsigned int header_size=0) const { #ifndef cimg_use_zlib if (is_compressed) cimg::warn(_cimglist_instance @@ -63104,12 +65306,10 @@ namespace cimg_library_suffixed { cimglist_instance); #endif CImgList stream; + if (header_size) CImg(1,header_size,1,1,0).move_to(stream); CImg tmpstr(128); const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; - if (std::strstr(ptype,"unsigned")==ptype) - cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); - else - cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype); + cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype); CImg::string(tmpstr,false).move_to(stream); cimglist_for(*this,l) { const CImg& img = _data[l]; @@ -63141,23 +65341,34 @@ namespace cimg_library_suffixed { if (failed_to_compress) { // Write in a non-compressed way CImg::string("\n",false).move_to(stream); stream.insert(1); - stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true); + stream.back(). + assign((unsigned char*)ref._data,ref._width,ref._height,ref._depth,ref._spectrum*sizeof(T),true); } } else CImg::string("\n",false).move_to(stream); } - cimglist_apply(stream,unroll)('y'); - return stream>'y'; + + // Determine best serialized image dimensions to store the whole buffer. + ulongT siz = 0; + cimglist_for(stream,l) siz+=stream[l].size(); + const ulongT max_siz = (ulongT)cimg::type::max(); + const unsigned int + nw = (unsigned int)(siz/max_siz + ((siz%max_siz)?1:0)), + nh = (unsigned int)(siz/nw + (siz%nw?1:0)); + CImg res(nw,nh,1,1,0); + unsigned char *ptr = res.data(); + cimglist_for(stream,l) { siz = stream[l].size(); std::memcpy(ptr,stream[l]._data,siz); ptr+=siz; } + return res; } //! Unserialize a CImg serialized buffer into a CImgList list. template - static CImgList get_unserialize(const CImg& buffer) { + static CImgList get_unserialize(const CImg& buffer, const unsigned int header_size=0) { #ifdef cimg_use_zlib #define _cimgz_unserialize_case(Tss) { \ Bytef *cbuf = 0; \ if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type::string()) { \ cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \ - for (ulongT k = 0; k::get_unserialize(): Specified serialized buffer is (null).", pixel_type()); CImgList res; - const t *stream = buffer._data, *const estream = buffer._data + buffer.size(); + const t *stream = buffer._data + header_size, *const estream = buffer._data + buffer.size(); bool loaded = false, endian = cimg::endianness(), is_bytef = false; CImg tmp(256), str_pixeltype(256), str_endian(256); *tmp = *str_pixeltype = *str_endian = 0; @@ -63215,7 +65428,7 @@ namespace cimg_library_suffixed { j = 0; while ((i=(int)*stream)!='\n' && stream::get_unserialize(): CImg header not found in serialized buffer.", @@ -63223,21 +65436,18 @@ namespace cimg_library_suffixed { if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; res.assign(N); - _cimg_unserialize_case("bool",bool); - _cimg_unserialize_case("unsigned_char",unsigned char); - _cimg_unserialize_case("uchar",unsigned char); - _cimg_unserialize_case("char",char); - _cimg_unserialize_case("unsigned_short",unsigned short); - _cimg_unserialize_case("ushort",unsigned short); - _cimg_unserialize_case("short",short); - _cimg_unserialize_case("unsigned_int",unsigned int); - _cimg_unserialize_case("uint",unsigned int); - _cimg_unserialize_case("int",int); - _cimg_unserialize_case("unsigned_int64",uint64T); - _cimg_unserialize_case("uint64",uint64T); - _cimg_unserialize_case("int64",int64T); - _cimg_unserialize_case("float",float); - _cimg_unserialize_case("double",double); + _cimg_unserialize_case("bool",0,0,cimg_uint8); + _cimg_unserialize_case("uint8","unsigned_char","uchar",cimg_uint8); + _cimg_unserialize_case("int8",0,0,cimg_int8); + _cimg_unserialize_case("char",0,0,char); + _cimg_unserialize_case("uint16","unsigned_short","ushort",cimg_uint16); + _cimg_unserialize_case("int16","short",0,cimg_int16); + _cimg_unserialize_case("uint32","unsigned_int","uint",cimg_uint32); + _cimg_unserialize_case("int32","int",0,cimg_int32); + _cimg_unserialize_case("uint64","unsigned_int64",0,cimg_uint64); + _cimg_unserialize_case("int64",0,0,cimg_int64); + _cimg_unserialize_case("float32","float",0,cimg_float32); + _cimg_unserialize_case("float64","double",0,cimg_float64); if (!loaded) throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined " "in serialized buffer.", @@ -63335,7 +65545,7 @@ namespace cimg_library_suffixed { // Find optimal font cache location to return. static CImgList fonts[16]; - static bool is_variable_widths[16] = { 0 }; + static bool is_variable_widths[16] = {}; ind = ~0U; for (int i = 0; i<16; ++i) if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) { @@ -63355,8 +65565,7 @@ namespace cimg_library_suffixed { basef.get_split('x',256).move_to(font); if (requested_height!=font[0]._height) cimglist_for(font,l) { - font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100, - font[0]._height>requested_height?2:5); + font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5); cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr]; } if (is_variable_width) { // Crop font @@ -63377,7 +65586,7 @@ namespace cimg_library_suffixed { if (' ' + 256 wpath(err); + CImg wpath((unsigned int)err); err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err); if (err) { // Convert 'mode' to a wide-character string err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0); if (err) { - CImg wmode(err); + CImg wmode((unsigned int)err); if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err)) return _wfopen(wpath,wmode); } @@ -63538,7 +65747,7 @@ namespace cimg_library_suffixed { // Try alternative method, with wide-character string. int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); if (err) { - CImg wpath(err); + CImg wpath((unsigned int)err); if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath); } } @@ -64046,7 +66255,7 @@ namespace cimg_library_suffixed { 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(s_path,s_path._width,"%s",p); \ cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ } @@ -64129,6 +66338,36 @@ namespace cimg_library_suffixed { return s_path; } +#if cimg_OS==2 + //! Get/set path to the \c powershell 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 *powershell_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 (win_searchpath("powershell.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\powershell.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"powershell.exe"); + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } +#endif // [internal] Sorting function, used by cimg::files(). inline int _sort_files(const void* a, const void* b) { @@ -64143,8 +66382,8 @@ namespace cimg_library_suffixed { const unsigned int siz = (unsigned int)std::strlen(filename); CImg format(16), body(siz + 32); const char *const ext = cimg::split_filename(filename,body); - if (*ext) cimg_snprintf(format,format.width(),"%%s_%%.%ud.%%s",digits); - else cimg_snprintf(format,format.width(),"%%s_%%.%ud",digits); + if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits); + else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits); cimg_snprintf(str,1024,format._data,body._data,number,ext); return str; } @@ -64276,17 +66515,19 @@ namespace cimg_library_suffixed { if (!file && !filename) throw CImgArgumentException("cimg::ftype(): Specified filename is (null)."); static const char - *const _pnm = "pnm", - *const _pfm = "pfm", *const _bmp = "bmp", + *const _cr2 = "cr2", + *const _dcm = "dcm", *const _gif = "gif", + *const _inr = "inr", *const _jpg = "jpg", *const _off = "off", *const _pan = "pan", + *const _pfm = "pfm", *const _png = "png", - *const _tif = "tif", - *const _inr = "inr", - *const _dcm = "dcm"; + *const _pnm = "pnm", + *const _tif = "tif"; + const char *f_type = 0; CImg header; const unsigned int omode = cimg::exception_mode(); @@ -64311,7 +66552,12 @@ namespace cimg_library_suffixed { else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG f_type = _png; - else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) // TIFF + else if (uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00 && // CR2 + uheader[4]==0x10 && uheader[5]==0x00 && uheader[6]==0x00 && uheader[7]==0x00 && + uheader[8]==0x43 && uheader[9]==0x52) + f_type = _cr2; + else if ((uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00) || + (uheader[0]==0x4D && uheader[1]==0x4D && uheader[2]==0x00 && uheader[3]==0x2A)) // TIFF f_type = _tif; else { // PNM or PFM CImgList _header = header.get_split(CImg::vector('\n'),0,false); @@ -64337,12 +66583,13 @@ namespace cimg_library_suffixed { \param timeout Maximum time (in seconds) authorized for downloading the file from the URL. \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure. \param referer Referer used, as a C-string. + \param user_agent User agent used, as a C-string. \return Value of \c filename_local. \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download. **/ inline char *load_network(const char *const url, char *const filename_local, const unsigned int timeout, const bool try_fallback, - const char *const referer) { + const char *const referer, const char *const user_agent) { if (!url) throw CImgArgumentException("cimg::load_network(): Specified URL is (null)."); if (!filename_local) @@ -64359,7 +66606,7 @@ namespace cimg_library_suffixed { do { cimg_snprintf(filename_local,256,"%s%c%s%s", cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data); - if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file); + if ((file = cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file); } while (file); #ifdef cimg_use_curl @@ -64377,9 +66624,11 @@ namespace cimg_library_suffixed { curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L); curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L); + curl_easy_setopt(curl,CURLOPT_MAXREDIRS,20L); 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); + if (user_agent) curl_easy_setopt(curl,CURLOPT_USERAGENT,user_agent); res = curl_easy_perform(curl); curl_easy_cleanup(curl); cimg::fseek(file,0,SEEK_END); // Check if file size is 0 @@ -64395,58 +66644,58 @@ namespace cimg_library_suffixed { 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 command((unsigned int)std::strlen(url) + 1024), s_referer, s_user_agent, s_timeout; cimg::unused(try_fallback); // Try with 'curl' first. - if (timeout) { - if (referer) - cimg_snprintf(command,command._width,"\"%s\" -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),referer,timeout,filename_local, - CImg::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"\"%s\" -m %u -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),timeout,filename_local, - CImg::string(url)._system_strescape().data()); - } else { - if (referer) - cimg_snprintf(command,command._width,"\"%s\" -e %s -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),referer,filename_local, - CImg::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"\"%s\" -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),filename_local, - CImg::string(url)._system_strescape().data()); + if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-m %u ",timeout); + else s_timeout.assign(1,1,1,1,0); + if (referer) cimg_snprintf(s_referer.assign(1024),1024,"-e %s ",referer); + else s_referer.assign(1,1,1,1,0); + if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"-A \"%s\" ",user_agent); + else s_user_agent.assign(1,1,1,1,0); + cimg_snprintf(command,command._width, + "\"%s\" -L --max-redirs 20 %s%s%s-f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local, + CImg::string(url)._system_strescape().data()); + cimg::system(command,cimg::curl_path()); + +#if cimg_OS==2 + if (cimg::fsize(filename_local)<=0) { // Try with 'powershell' otherwise. + if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-TimeoutSec %u ",timeout); + else s_timeout.assign(1,1,1,1,0); + if (referer) cimg_snprintf(s_referer.assign(1024),1024,"-Headers @{'Referer'='%s'} ",referer); + else s_referer.assign(1,1,1,1,0); + if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"-UserAgent \"%s\" ",user_agent); + else s_user_agent.assign(1,1,1,1,0); + cimg_snprintf(command,command._width, + "\"%s\" -NonInteractive -Command Invoke-WebRequest %s%s%s-OutFile \"%s\" -Uri \"%s\"", + cimg::powershell_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local, + CImg::string(url)._system_strescape().data()); + cimg::system(command,cimg::powershell_path()); } - cimg::system(command, cimg::curl_path()); +#endif - if (!(file=cimg::std_fopen(filename_local,"rb"))) { + if (cimg::fsize(filename_local)<=0) { // Try with 'wget' otherwise. + if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-T %u ",timeout); + else s_timeout.assign(1,1,1,1,0); + if (referer) cimg_snprintf(s_referer.assign(1024),1024,"--referer=%s ",referer); + else s_referer.assign(1,1,1,1,0); + if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"--user-agent=\"%s\" ",user_agent); + else s_user_agent.assign(1,1,1,1,0); + cimg_snprintf(command,command._width, + "\"%s\" --max-redirect=20 %s%s%s-q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local, + CImg::string(url)._system_strescape().data()); + cimg::system(command,cimg::wget_path()); - // Try with 'wget' otherwise. - if (timeout) { - if (referer) - cimg_snprintf(command,command._width,"\"%s\" --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),referer,timeout,filename_local, - CImg::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"\"%s\" -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),timeout,filename_local, - CImg::string(url)._system_strescape().data()); - } else { - if (referer) - cimg_snprintf(command,command._width,"\"%s\" --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),referer,filename_local, - CImg::string(url)._system_strescape().data()); - else - cimg_snprintf(command,command._width,"\"%s\" -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),filename_local, - CImg::string(url)._system_strescape().data()); - } - cimg::system(command, cimg::wget_path()); - - if (!(file=cimg::std_fopen(filename_local,"rb"))) + if (cimg::fsize(filename_local)<=0) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands " +#if cimg_OS==2 + "'wget', 'curl', or 'powershell'.",url); +#else "'wget' or 'curl'.",url); +#endif cimg::fclose(file); // Try gunzip it. @@ -64454,7 +66703,7 @@ namespace cimg_library_suffixed { std::rename(filename_local,command); cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"", gunzip_path(),filename_local); - cimg::system(command, gunzip_path()); + cimg::system(command,gunzip_path()); file = cimg::std_fopen(filename_local,"rb"); if (!file) { cimg_snprintf(command,command._width,"%s.gz",filename_local); @@ -64462,11 +66711,8 @@ namespace cimg_library_suffixed { file = cimg::std_fopen(filename_local,"rb"); } } - cimg::fseek(file,0,SEEK_END); // Check if file size is 0 - if (std::ftell(file)<=0) - throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands " - "'wget' or 'curl'.",url); - cimg::fclose(file); + + if (file) cimg::fclose(file); return filename_local; } @@ -64660,7 +66906,7 @@ namespace cimg_library_suffixed { draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black); if (logo._data) canvas.draw_image(lx,ly,logo); - unsigned int xbuttons[6] = { 0 }; + unsigned int xbuttons[6] = {}; cimglist_for(buttons,lll) { xbuttons[lll] = bx + (bw + 12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); @@ -64740,8 +66986,8 @@ namespace cimg_library_suffixed { \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) { @@ -64759,7 +67005,7 @@ namespace cimg_library_suffixed { } // namespace cimg_library { ... //! Short alias name. -namespace cil = cimg_library_suffixed; +namespace cil = cimg_library; #ifdef _cimg_redefine_False #define False 0 diff --git a/external/CImg/README.md b/external/CImg/README.md index a05bd7320..06b3fa78f 100644 --- a/external/CImg/README.md +++ b/external/CImg/README.md @@ -1,9 +1,10 @@ -![Logo](http://cimg.eu/img/CImgLogo2.jpg) +![Logo](http://cimg.eu/img/logo_header.jpg) ##### http://cimg.eu ------------------ - -The **CImg Library** is a **small** and **open-source** **C++ toolkit** for **image processing**, designed with these properties in mind: +![Build](https://github.com/GreycLab/CImg/workflows/Build%20then%20test/badge.svg) +------------------ +The **CImg Library** is a **small** and **open-source** **C++ library** for **image processing**, designed with these properties in mind: ![Usefulness](http://cimg.eu/img/item_usefulness.jpg) **CImg** defines *classes* and *methods* to manage images in your own C++ code. You can use **CImg** to load/save various file formats, access pixel values, display/transform/filter images, draw primitives (text, faces, curves, 3d objects, ...), compute statistics, manage user interactions on images, and so on... @@ -11,7 +12,7 @@ The **CImg Library** is a **small** and **open-source** **C++ toolkit** for **im ![Portability](http://cimg.eu/img/item_portability.jpg) **CImg** is *self-contained*, *thread-safe* and *highly portable*. It fully works on *different operating systems* (`Unix,Windows,MacOS X,*BSD,...`) and is compatible with *various C++ compilers* (`Visual C++,g++,clang++,icc,...`). -![Simplicity](http://cimg.eu/img/item_simplicity.jpg) **CImg** is *lightweight*. It is made of a single header file [`CImg.h`](https://github.com/dtschump/CImg/raw/master/CImg.h) that must be included in your C++ source. It defines only *four* different classes, encapsulated in the namespace `cimg_library`. It can be compiled using a minimal set of standard C++ and system libraries only. *No need for exotic or complex dependencies*. +![Simplicity](http://cimg.eu/img/item_simplicity.jpg) **CImg** is *lightweight*. It is made of a single header file [`CImg.h`](https://github.com/GreycLab/CImg/raw/master/CImg.h) that must be included in your C++ source. It defines only *four* different classes, encapsulated in the namespace `cimg_library`. It can be compiled using a minimal set of standard C++ and system libraries only. *No need for exotic or complex dependencies*. ![Extensibility](http://cimg.eu/img/item_extensibility.jpg) Although not mandatory, **CImg** can use functionalities of external tools/libraries such as [Board](http://libboard.sourceforge.net/), [FFMPEG](http://ffmpeg.mplayerhq.hu/), [FFTW3](http://www.fftw.org/), [GraphicsMagick](http://www.graphicsmagick.org/), [ImageMagick](http://www.imagemagick.org/), [Lapack](http://www.netlib.org/lapack/), [libcurl](http://curl.haxx.se/libcurl/), [libjpeg](http://www.ijg.org/), [libpng](http://www.libpng.org/pub/png/libpng.html), [libtiff](http://www.libtiff.org/), [Magick++](http://www.imagemagick.org/Magick++/), [OpenEXR](http://www.openexr.com/), [OpenCV](http://http://opencv.willowgarage.com/wiki/), [OpenMP](http://www.openmp.org/) or [XMedCon](http://xmedcon.sourceforge.net/). Moreover, a simple *plug-in* mechanism allows any user to directly enhance the library capabilities according to their needs. diff --git a/external/CImg/examples/CMakeLists.txt b/external/CImg/examples/CMakeLists.txt index 162029541..38b5894e3 100644 --- a/external/CImg/examples/CMakeLists.txt +++ b/external/CImg/examples/CMakeLists.txt @@ -106,6 +106,9 @@ SET(CIMG_MAGICK_CCFLAGS -Dcimg_use_magick) # ( http://www.fftw.org/ ) SET(CIMG_FFTW3_CCFLAGS -Dcimg_use_fftw3) +# Flags to enable native support for HEIC image files, using libheif. +# ( https://github.com/strukturag/libheif ) +SET(CIMG_HEIC_CCFLAGS -Dcimg_use_heic) @@ -118,6 +121,22 @@ FIND_PACKAGE(ZLIB) FIND_PACKAGE(LAPACK) FIND_PACKAGE(BLAS) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_HEIF libheif QUIET) +endif() + +find_path(HEIF_INCLUDE_DIR NAMES libheif/heif_cxx.h + PATHS ${PC_HEIF_INCLUDEDIR}) +find_library(HEIF_LIBRARY NAMES heif libheif + PATHS ${PC_HEIF_LIBDIR}) + +set(HEIF_VERSION ${PC_HEIF_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(HEIF + REQUIRED_VARS HEIF_LIBRARY HEIF_INCLUDE_DIR + VERSION_VAR HEIF_VERSION) + PKG_CHECK_MODULES(FFTW3 fftw3) PKG_CHECK_MODULES(OPENEXR OpenEXR) PKG_CHECK_MODULES(MAGICK Magick++) @@ -200,7 +219,13 @@ if(MAGICK_FOUND) SET( SYSTEM_LIBS ${SYSTEM_LIBS} ${MAGICK_LIBRARIES} ) endif() - +if(HEIF_FOUND) + get_filename_component(HEIF_LIB_DIRS ${HEIF_LIBRARY} PATH) + SET(CIMG_CFLAGS "${CIMG_CFLAGS} ${CIMG_HEIC_CCFLAGS}") + link_directories( ${HEIF_LIB_DIRS} ) + include_directories( ${HEIF_INCLUDE_DIRS} ) + SET( SYSTEM_LIBS ${SYSTEM_LIBS} ${HEIF_LIBRARY} ) +endif() if( LIBAVCODEC_FOUND AND LIBAVFORMAT_FOUND AND LIBSWSCALE_FOUND AND LIBAVUTIL_FOUND ) diff --git a/external/CImg/examples/Makefile b/external/CImg/examples/Makefile index 702ac363c..75e8a36a7 100644 --- a/external/CImg/examples/Makefile +++ b/external/CImg/examples/Makefile @@ -153,7 +153,11 @@ VT100_CFLAGS = -Dcimg_use_vt100 # Flags to enable code optimization by the compiler. OPT_CFLAGS = -Ofast ifdef IS_GCC -OPT_CFLAGS = -Ofast -mtune=generic +# Add -mtune=generic for GCC if supported. +NO_MTUNE_GENERIC = $(shell $(CXX) -mtune=generic -E - < /dev/null > /dev/null 2>&1; echo $$?) +ifeq ($(NO_MTUNE_GENERIC),0) +OPT_CFLAGS += -mtune=generic +endif endif ifdef IS_ICPC OPT_CFLAGS = -fast diff --git a/external/CImg/examples/jawbreaker.cpp b/external/CImg/examples/jawbreaker.cpp index 187a35268..2f8d2ae76 100644 --- a/external/CImg/examples/jawbreaker.cpp +++ b/external/CImg/examples/jawbreaker.cpp @@ -99,6 +99,7 @@ int main(int argc, char **argv) { CImg board, previous_board, selected_board, shape, img(background); CImgDisplay disp(img.width(),img.height(),"Jawbreaker",0); bool redraw = true, gameover = false, title = true; + for (float opac = 0.0f; !disp.is_closed(); ) { // Init board @@ -113,10 +114,12 @@ int main(int argc, char **argv) { if (redraw) { (img=background).draw_text(2,2,"Score : %u",yellow,0,0.7f,24,score). draw_text(Wi - 90,2,"Best : %u",orange,0,0.9f,17,best_score); + if (selected_board) { cimg_forXY(selected_board,x,y) if (selected_board(x,y)) img.draw_image(x<<5,y<<5,balls[selected_board(x,y)],mask); } else cimg_forXY(board,x,y) if (board(x,y)) img.draw_image(x<<5,y<<5,balls[board(x,y)],mask); + if (title) { CImg text1, text2; text1.draw_text(0,0,"- Jawbreaker -",white,0,1,48).resize(-100,-100,1,3); diff --git a/external/CImg/html/CImg_documentation.h b/external/CImg/html/CImg_documentation.h index fcdd056c4..3f100daff 100644 --- a/external/CImg/html/CImg_documentation.h +++ b/external/CImg/html/CImg_documentation.h @@ -136,7 +136,7 @@ The CImg library is a very light and user-friendly library : only standard system libraries are used. It avoids handling complex dependencies and problems with library compatibility. - The only thing you need is a (quite modern) C++ compiler : + The only thing you need is a C++ compiler : - Microsoft Visual Studio.NET and Visual Express Edition : Use the project files and solution files provided in the %CImg Library package (directory 'compilation/') to see how it works. @@ -163,7 +163,7 @@ - Dev-Cpp : Use the project file provided in the CImg library package to see how it works. If you are using other compilers and encounter problems, please - write me since maintaining compatibility is one + write me since maintaining compatibility is one of the priorities of the %CImg Library. Nevertheless, old compilers that do not respect the C++ standard will not support the %CImg Library. @@ -208,10 +208,10 @@ \subsection ssf11 1.1. What is the CImg Library ? - The CImg Library is an open-source C++ toolkit for image processing.\n + The CImg Library is an open-source C++ library for image processing.\n It mainly consists in a (big) single header file - CImg.h + CImg.h providing a set of C++ classes and functions that can be used in your own sources, to load/save, manage/process and display generic images. It's actually a very simple and pleasant toolkit for coding image processing stuff in C++ : @@ -237,10 +237,10 @@ The package is distributed under the CeCILL license. This package contains : - - The main library file CImg.h (C++ header file). - - Several C++ source code showing examples of using CImg. + - The main library file CImg.h (C++ header file). + - Several C++ source code showing examples of using CImg. - A complete library documentation, in PDF format. - - Additional library plug-ins that can be used to extend + - Additional library plug-ins that can be used to extend library capabilities for specific uses. The CImg Library is a quite lightweight library which is easy to maintain (due to its particular structure), and thus @@ -266,10 +266,10 @@ corresponding to different constraints on the source files : - The CeCILL-C license is the most permissive one, close to the GNU LGPL license, and applies only on the main library file - CImg.h. - Basically, this license allows to use CImg.h + CImg.h. + Basically, this license allows to use CImg.h in a closed-source product without forcing you to redistribute the entire software source code. Anyway, - if one modifies the CImg.h source file, one has to redistribute + if one modifies the CImg.h source file, one has to redistribute the modified version of the file that must be governed by the same CeCILL-C license. - The CeCILL license applies to all other files @@ -314,7 +314,7 @@ \subsection ssf23 2.3 Why is CImg entirely contained in a single header file ? People are often surprised to see that the complete code of the library is contained in a single (big) C++ header file - CImg.h. + CImg.h. There are good practical and technical reasons to do that. Some arguments are listed below to justify this approach, so (I hope) you won't think this is a awkwardly C++ design of the CImg library :\n diff --git a/external/CImg/html/download.html b/external/CImg/html/download.html index 132ad18d5..d916af7a2 100644 --- a/external/CImg/html/download.html +++ b/external/CImg/html/download.html @@ -40,7 +40,7 @@ It contains all the required files, as well as various examples (which must be compiled), illustrating the use of the library functions and classes.
- + Sources Repository @@ -50,7 +50,7 @@ updates as well. To do this, just type the command : in your favorite console. Nevertheless, you have to know that some code in the source repository is under development and may be experimental, so always test the latest stable archive before complaining ! diff --git a/external/CImg/html/header.html b/external/CImg/html/header.html index 43e625dcb..b04d1d934 100644 --- a/external/CImg/html/header.html +++ b/external/CImg/html/header.html @@ -23,7 +23,7 @@
Logo

- Latest stable version: 2.9.6        Current pre-release: 2.9.7 + Latest stable version: 3.1.6        Current pre-release: 3.2.0


@@ -49,7 +49,7 @@
  • Documentation   Documentation
  • -
  • +
  • Report Issue   Report Issue
  • diff --git a/external/CImg/html/header_doxygen.html b/external/CImg/html/header_doxygen.html index 6737ed90c..42a695d03 100644 --- a/external/CImg/html/header_doxygen.html +++ b/external/CImg/html/header_doxygen.html @@ -26,7 +26,7 @@
    Logo

    - Latest stable version: 2.9.6        Current pre-release: 2.9.7 + Latest stable version: 3.1.6        Current pre-release: 3.2.0


    @@ -52,7 +52,7 @@
  • Documentation   Documentation
  • -
  • +
  • Report Issue   Report Issue
  • diff --git a/external/CImg/html/img/logoCImg0.png b/external/CImg/html/img/logoCImg0.png new file mode 100644 index 000000000..d687033a1 Binary files /dev/null and b/external/CImg/html/img/logoCImg0.png differ diff --git a/external/CImg/html/img/logo_header.jpg b/external/CImg/html/img/logo_header.jpg index bdf9e016d..caccf993c 100644 Binary files a/external/CImg/html/img/logo_header.jpg and b/external/CImg/html/img/logo_header.jpg differ diff --git a/external/CImg/html/img/logo_header.xcf b/external/CImg/html/img/logo_header.xcf new file mode 100644 index 000000000..83381903d Binary files /dev/null and b/external/CImg/html/img/logo_header.xcf differ diff --git a/external/CImg/html/img/logo_header_old.jpg b/external/CImg/html/img/logo_header_old.jpg new file mode 100644 index 000000000..bdf9e016d Binary files /dev/null and b/external/CImg/html/img/logo_header_old.jpg differ diff --git a/external/CImg/html/img/postcard75.jpg b/external/CImg/html/img/postcard75.jpg new file mode 100644 index 000000000..6e7ced141 Binary files /dev/null and b/external/CImg/html/img/postcard75.jpg differ diff --git a/external/CImg/html/img/postcard76.jpg b/external/CImg/html/img/postcard76.jpg new file mode 100644 index 000000000..9ce1a8294 Binary files /dev/null and b/external/CImg/html/img/postcard76.jpg differ diff --git a/external/CImg/html/img/postcard77.jpg b/external/CImg/html/img/postcard77.jpg new file mode 100644 index 000000000..bac7581e8 Binary files /dev/null and b/external/CImg/html/img/postcard77.jpg differ diff --git a/external/CImg/html/img/postcard78.jpg b/external/CImg/html/img/postcard78.jpg new file mode 100644 index 000000000..2e8ab1238 Binary files /dev/null and b/external/CImg/html/img/postcard78.jpg differ diff --git a/external/CImg/html/img/postcard79.jpg b/external/CImg/html/img/postcard79.jpg new file mode 100644 index 000000000..3c5ff88b3 Binary files /dev/null and b/external/CImg/html/img/postcard79.jpg differ diff --git a/external/CImg/html/img/postcard80.jpg b/external/CImg/html/img/postcard80.jpg new file mode 100644 index 000000000..41f0332b4 Binary files /dev/null and b/external/CImg/html/img/postcard80.jpg differ diff --git a/external/CImg/html/index.html b/external/CImg/html/index.html index 52e1898af..39518763b 100644 --- a/external/CImg/html/index.html +++ b/external/CImg/html/index.html @@ -4,7 +4,7 @@ - The CImg Library - C++ Template Image Processing Toolkit + The CImg Library - C++ Template Image Processing Library @@ -30,7 +30,7 @@

    The Library is a small and open-source - C++ toolkit for image processing, + C++ library for image processing, designed with these properties in mind :

    @@ -59,7 +59,7 @@ Simplicity
    is lightweight. It is made of a single header file -
    CImg.h + CImg.h that must be included in your C++ source. It defines only four different classes, encapsulated in the namespace cimg_library. It can be compiled using a minimal set of standard C++ and system libraries only.
    @@ -291,7 +291,7 @@ CImg Flyer at work, in your lab or school (available in .PDF or .JPEG formats).
  • You can report bugs, propose patches or new functionalities, using the - forum.
  • + forum.
  • You can write tutorials or parts of the documentation.
  • @@ -437,13 +437,28 @@
  • Pregonda / Menorca, from Josep Febrer.
  • - USA, from Patrick Wanters.
  • + USA, from Patrick Wauters.
  • Toulon/France, from Cyril Prissette.
  • Bochum/Germany, from Andreas Weissenburger.
  • Portsmouth/USA, from Benjamin Russell.
  • +
  • + Angers/France, from Bruno Bianchi.
  • +
  • + Makuhari/Japan, from Hiroyuki Hayashi.
  • +
  • + Tristan Da Cunha, from Richard Lockwood.
  • +
  • + Tyrol/Austria, from Marcel.
  • +
  • + Heidelberg/Germany, from Markus.
  • +
  • + Jokulsarlon Lagoonl/Iceland, from Patrick Wauters.
  • +
  • + Argentina, from Amyspark.
  • + diff --git a/external/CImg/html/screenshots.html b/external/CImg/html/screenshots.html index bcb54e5b0..8d746c2f4 100644 --- a/external/CImg/html/screenshots.html +++ b/external/CImg/html/screenshots.html @@ -50,7 +50,7 @@ : The main demo sample of the package (1433 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp

    Contains 26 different real-time animations, as well as a nice selection menu.

    @@ -70,7 +70,7 @@ : A 2d bump-mapping effect (30 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp

    You can move the light source using the mouse. This sample shows how to handle mouse motion and create an animation in a window. It demonstrates also that the Library is quite fast ! @@ -92,7 +92,7 @@ : Computation of the Hough Transform (95 lines of code).
    : - hough_transform.cpp + hough_transform.cpp

    Illustrate the computation of the Hough transform to detect lines in 2D images. Provide also simple user interface to select and display lines. @@ -108,7 +108,7 @@ : Do a fading between two images (35 lines of code).
    : - fade_images.cpp + fade_images.cpp

    Very small code to perform a funny effect. Also demonstrate how to easily deal with command line arguments.

    @@ -123,7 +123,7 @@ : A functional Mandelbrot fractal explorer (51 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp

    Show how to use the predefined feature selection function present in the Library.

    @@ -141,7 +141,7 @@ : A simple shoot-em-up game, featuring the people of the Robotvis/Odyssee Lab (180 lines of code).
    : - odykill.cpp + odykill.cpp

    Another demonstration of handling mouse and creating animation for pedagogic purposes.

    @@ -157,7 +157,7 @@ An implementation of an image registration algorithm, with multiscale capability (201 lines of code).
    : - image_registration.cpp + image_registration.cpp

    Compute a motion map between two images, and warp one into the another through a smooth animation.

    @@ -172,7 +172,7 @@ : A mini-painting program (30 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp

    This is not a replacement to Photoshop, but it already includes a filling algorithm as well as a color selection tool. @@ -191,7 +191,7 @@ : A classical demomaking-effect, called 'rotozoom' (20 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp

    Smell the old school parfume of the atari/amiga demos.

    @@ -206,7 +206,7 @@ : An animation featuring triangles that are rotating (50 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp

    Could be a replacement to your classical screen saver.

    @@ -221,7 +221,7 @@ : A demo of image filtering in the Fourier Domain (30 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -237,7 +237,7 @@ : An example of real-time 3D rendering.
    : - CImg_demo.cpp + CImg_demo.cpp

    No use of OpenGL or VTK in this example, only pure software functions are used !

    @@ -255,7 +255,7 @@ : An example of the ellipse drawing function, used to bounce an elastic bubble (25 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -271,7 +271,7 @@ : A real-time 3D virtual landscape (40 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp

    The altitude map is based on a 'fractal plasma' generator.

    @@ -289,7 +289,7 @@ : A nice plasma effect with a sinus scroller (70 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -305,7 +305,7 @@ : An implementation of the well known Tetris game (130 lines of code).
    : - tetris.cpp + tetris.cpp

    Very small code for a complete version of the Tetris game.

    @@ -323,7 +323,7 @@ : The implementation of the Tschumperlé-Deriche algorithm for image restoration and inpainting (170 lines of code).
    : - pde_TschumperleDeriche2d.cpp + pde_TschumperleDeriche2d.cpp

    See the corresponding publication for more detail on the algorithm. @@ -339,7 +339,7 @@ : A very smart and classical demo effect called 'Shade bobs' (60 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -355,7 +355,7 @@ : A nice Blob Editor in only few lines (90 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -371,7 +371,7 @@ : 3d Metaballs animation (23 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -387,7 +387,7 @@ : A tool to visualize images as surfaces in 3d (100 lines of code).
    : - image_surface.cpp + image_surface.cpp
    Click here to see the video @@ -403,7 +403,7 @@ : A 3d viewer for Diffusion tensor imaging datasets (526 lines of code).
    : - dtmri_view.cpp + dtmri_view.cpp
    Click here to see the video @@ -419,7 +419,7 @@ : Applying the wave equation on an 3d image-mapped surface (55 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -435,7 +435,7 @@ : A simple 2d curve editor using spline interpolation (300 lines of code).
    : - curve_editor.cpp + curve_editor.cpp @@ -447,7 +447,7 @@ : A complete and funny game featuring colored balls (121 lines of code).
    : - jawbreaker.cpp + jawbreaker.cpp
    Click here to see the video @@ -463,7 +463,7 @@ : A cool 3d reflection effect, using some 3d object rendering tricks (130 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp
    Click here to see the video @@ -479,7 +479,7 @@ : A simple word puzzle game (100 lines of code).
    : - CImg_demo.cpp + CImg_demo.cpp diff --git a/external/CImg/plugins/cvMat.h b/external/CImg/plugins/cvMat.h index aba70cfc1..07d5660f8 100644 --- a/external/CImg/plugins/cvMat.h +++ b/external/CImg/plugins/cvMat.h @@ -7,23 +7,16 @@ # conversions for generic image types # ( IPL = Intel Performance Library ) # This file is a part of the CImg Library project. -# ( http://cimg.eu ) +# ( http://cimg.sourceforge.net ) # # Copyright : Alberto Albiol # alalbiol@iteam.upv.es # # How to use : In the main program include: -# OPENCV 2.4.x # #include "cv.h" # #include "highgui.h" # #define cimg_plugin1 "cvMat.h" # #include "CImg.h" -# -# OPENCV 3.x.x -# #include -# #define cimg_plugin1 "cvMat.h" -# #include "CImg.h" - */ #ifndef cimg_plugin_cvMat #define cimg_plugin_cvMat @@ -37,14 +30,14 @@ CImg(const cv::Mat& src):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared( CImg& assign(const cv::Mat & src) { if (src.isContinuous()) { switch (src.depth()) { - // case CV_1U: { // 1-bit int + // case CV_1U: { // 1-bit int. // IplImage *src1 = cvCreateImage(cvGetSize(src),CV_8U,1); // cvConvert(src,src1); // CImg((unsigned char*)src1->imageData,src1->nChannels,src1.cols,src1.rows,1,true). // get_permute_axes("yzcx").move_to(*this); // cvReleaseImage(&src1); // } break; - case CV_8U: // 8-bit unsigned int + case CV_8U: // 8-bit unsigned int. if (src.channels()==1) { CImg((unsigned char*)src.ptr(),src.cols,src.rows,true).move_to(*this); } else { @@ -61,7 +54,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_8S: // 8-bit signed int + case CV_8S: // 8-bit signed int. if (src.channels()==1) { CImg((char*)src.ptr(),src.cols,src.rows,true).move_to(*this); } else { @@ -78,7 +71,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_16U: // 16-bit unsigned int + case CV_16U: // 16-bit unsigned int. if (src.channels()==1) { CImg((unsigned short*)src.ptr(),src.cols,src.rows,true).move_to(*this); } else { @@ -95,7 +88,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_16S: // 16-bit signed int + case CV_16S: // 16-bit signed int. if (src.channels()==1) { CImg((short*)src.ptr(),src.cols,src.rows,true).move_to(*this); } else { @@ -112,7 +105,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_32S: // 32-bit signed int + case CV_32S: // 32-bit signed int. if (src.channels()==1) { CImg((int*)src.ptr(),src.cols,src.rows,true).move_to(*this); } else { @@ -129,7 +122,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_32F: // 32-bit float + case CV_32F: // 32-bit float. if (src.channels()==1) { CImg((float*)src.ptr(),src.cols,src.rows,true).move_to(*this); } else { @@ -146,7 +139,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_64F: // 64-bit double + case CV_64F: // 64-bit double. if (src.channels()==1) { CImg((double*)src.ptr(),src.cols,src.rows,true).move_to(*this); } else { @@ -172,7 +165,7 @@ CImg& assign(const cv::Mat & src) { } else { cv::Size size = src.size(); switch (src.depth()) { - case CV_8U: // 8-bit unsigned int + case CV_8U: // 8-bit unsigned int. if (src.channels()==1) { CImg tmp(src.cols,src.rows); for (int i = 0; i& assign(const cv::Mat & src) { std::vector channels; cv::split(src,channels); for (int c = 0; c plane = tmp.get_shared_channel(src.channels() - 1 - c); + CImg plane = tmp.get_shared_channel(src.channels()-1-c); for (int i = 0; i(i); unsigned char *row_o = plane.data(0,i); @@ -196,7 +189,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_8S: // 8-bit int + case CV_8S: // 8-bit int. if (src.channels()==1) { CImg tmp(src.cols,src.rows); for (int i = 0; i& assign(const cv::Mat & src) { std::vector channels; cv::split(src,channels); for (int c = 0; c plane = tmp.get_shared_channel(src.channels() - 1 - c); + CImg plane = tmp.get_shared_channel(src.channels()-1-c); for (int i = 0; i(i); char *row_o = plane.data(0,i); @@ -220,7 +213,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_16S: // 16-bit int + case CV_16S: // 16-bit int. if (src.channels()==1) { CImg tmp(src.cols,src.rows); for (int i = 0; i& assign(const cv::Mat & src) { std::vector channels; cv::split(src,channels); for (int c = 0; c plane = tmp.get_shared_channel(src.channels() - 1 - c); + CImg plane = tmp.get_shared_channel(src.channels()-1-c); for (int i = 0; i(i); short *row_o = plane.data(0,i); @@ -244,7 +237,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_32F: // 32-bit float + case CV_32F: // 32-bit float.float if (src.channels()==1) { CImg tmp(src.cols,src.rows); for (int i = 0; i& assign(const cv::Mat & src) { std::vector channels; cv::split(src,channels); for (int c = 0; c plane = tmp.get_shared_channel(src.channels() - 1 - c); + CImg plane = tmp.get_shared_channel(src.channels()-1-c); for (int i = 0; i(i); float *row_o = plane.data(0,i); @@ -268,7 +261,7 @@ CImg& assign(const cv::Mat & src) { tmp.move_to(*this); } break; - case CV_64F: // 64-bit double + case CV_64F: // 64-bit double. if (src.channels()==1) { CImg tmp(src.cols,src.rows); for (int i = 0; i& assign(const cv::Mat & src) { std::vector channels; cv::split(src,channels); for (int c = 0; c plane = tmp.get_shared_channel(src.channels() - 1 - c); + CImg plane = tmp.get_shared_channel(src.channels()-1-c); for (int i = 0; i(i); double *row_o = plane.data(0,i); @@ -326,12 +319,19 @@ cv::Mat get_MAT(const unsigned int z=0) const { matType=-1; if (!cimg::strcasecmp(buf.pixel_type(),"unsigned char")) matType = CV_8UC1; + if (!cimg::strcasecmp(buf.pixel_type(),"uint8")) matType = CV_8UC1; if (!cimg::strcasecmp(buf.pixel_type(),"char")) matType = CV_8SC1; + if (!cimg::strcasecmp(buf.pixel_type(),"int8")) matType = CV_8SC1; if (!cimg::strcasecmp(buf.pixel_type(),"unsigned short")) matType = CV_16UC1; + if (!cimg::strcasecmp(buf.pixel_type(),"uint16")) matType = CV_16UC1; if (!cimg::strcasecmp(buf.pixel_type(),"short")) matType = CV_16SC1; + if (!cimg::strcasecmp(buf.pixel_type(),"int16")) matType = CV_16SC1; if (!cimg::strcasecmp(buf.pixel_type(),"int")) matType = CV_32SC1; + if (!cimg::strcasecmp(buf.pixel_type(),"int32")) matType = CV_32SC1; if (!cimg::strcasecmp(buf.pixel_type(),"float")) matType = CV_32FC1; + if (!cimg::strcasecmp(buf.pixel_type(),"float32")) matType = CV_32FC1; if (!cimg::strcasecmp(buf.pixel_type(),"double")) matType = CV_64FC1; + if (!cimg::strcasecmp(buf.pixel_type(),"float64")) matType = CV_64FC1; if (matType<0) throw CImgInstanceException(_cimg_instance "get_MAT() : pixel type '%s' is not supported.", @@ -340,7 +340,7 @@ cv::Mat get_MAT(const unsigned int z=0) const { std::vector channels(nchannels); if (nchannels>1) { for (int c = 0; c(buf.data() + rows*cols*(nchannels - 1 - c))); + channels[c] = cv::Mat(rows,cols,matType,const_cast(buf.data()+rows*cols*(nchannels-1-c))); } // for channels cv::merge(channels,out); } else out = cv::Mat(rows,cols,matType,const_cast(buf.data())).clone(); diff --git a/external/CImg/plugins/tiff_stream.h b/external/CImg/plugins/tiff_stream.h index 1a47ada35..a72e11419 100644 --- a/external/CImg/plugins/tiff_stream.h +++ b/external/CImg/plugins/tiff_stream.h @@ -159,7 +159,7 @@ CImg& load_tiff(std::istream* tiffInStream, TIFFSetDirectory(tif,0); CImg frame; for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { - frame._load_tiff(tif,l,0,0); + frame._load_tiff(tif,l,0,0,0); if (l==nfirst_frame) assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum); if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum) diff --git a/external/CImg/resources/cimg_buildpackage b/external/CImg/resources/cimg_buildpackage index 0372e877c..f83d80566 100755 --- a/external/CImg/resources/cimg_buildpackage +++ b/external/CImg/resources/cimg_buildpackage @@ -141,7 +141,7 @@ echo -e "\n** Log generated by 'doxygen' **\n\n">>$LOG_FILE echo " - Build reference documentation in PDF format." cd $SRC_DIR/html/latex -gmic it structcimg__library_1_1CImg.tex replace_str \"operator%\",\"operator\\\\%\" ot structcimg__library_1_1CImg.tex +# gmic it structcimg__library_1_1CImg.tex replace_str \"operator%\",\"operator\\\\%\" ot structcimg__library_1_1CImg.tex echo -e "\n** Log generated by 'latex' **\n\n">>$LOG_FILE make>>$LOG_FILE 2>&1 cp -f refman.pdf ../CImg_reference.pdf @@ -152,6 +152,8 @@ rm -rf ../latex echo " - Commit on GIT repository." cd $SRC_DIR if [ "$1" == "final" ]; then + git push --delete origin v.$SVERSION + git pull git tag -d v.$SVERSION git tag v.$SVERSION git commit -m "Final release "${SVERSION} >>$LOG_FILE 2>&1 @@ -175,12 +177,12 @@ mv $ZIP_FILE $BASE_DIR # Copy files to CImg server. cd $BASE_DIR if [ "$1" == "final" ]; then - lftp ftp://$GMIC_LOGIN:$GMIC_PASSWD@$GMIC_FTP -e "put -O /www/CImg/files/ ${ZIP_FILE}; quit"; + lftp sftp://$GMIC_LOGIN:@ovh -e "put -O /home/"$GMIC_LOGIN"/www/CImg/files/ ${ZIP_FILE}; quit"; fi -lftp ftp://$GMIC_LOGIN:$GMIC_PASSWD@$GMIC_FTP -e "put -O /www/CImg/files/ ${ZIP_FILE} -o CImg_latest.zip; quit" +lftp sftp://$GMIC_LOGIN:@ovh -e "put -O /home/"$GMIC_LOGIN"/www/CImg/files/ ${ZIP_FILE} -o CImg_latest.zip; quit" cd $SRC_DIR/html/ -lftp ftp://$GMIC_LOGIN:$GMIC_PASSWD@$GMIC_FTP -e "mirror -RL . /www/CImg/ ; quit" +lftp sftp://$GMIC_LOGIN:@ovh -e "mirror -RL . /home/"$GMIC_LOGIN"/www/CImg/ ; quit" # End of build script echo -e " - All done, you should look at the LOG file '$LOG_FILE'.\n" diff --git a/external/CImg/resources/cimg_ftp b/external/CImg/resources/cimg_ftp index 9879f1dc2..af37ad0ee 100755 --- a/external/CImg/resources/cimg_ftp +++ b/external/CImg/resources/cimg_ftp @@ -1,4 +1,4 @@ -#!/usr/bin/gmic +#!/usr/bin/env gmic skip "${1=}" @@ -19,21 +19,22 @@ pwd={`" # Upload file or directory. if "s = ['$1']; "$is_pwd" && (s==0 || s=='.')" # Synchronize current directory e[] "Synchronize current directory '"${pwd}"' with 'SERVER"${pwd}"'.\n" - com="lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -RL . /www/CImg"${pwd}" ; quit\"" + com="lftp sftp://"$GMIC_LOGIN":@ovh -e \"mirror -RL . /home/"$GMIC_LOGIN"/www/CImg"${pwd}" ; quit\"" x $com elif $is_pwd" && isdir(['"$1"'])" # Synchronize specified directory e[] "Synchronize directory '$1' with 'SERVER"${pwd}"$1'.\n" - com="lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -RL \\\"$1\\\" \\\"/www/CImg"${pwd}"$1\\\" ; quit\"" + com="lftp sftp://"$GMIC_LOGIN":@ovh -e \"mirror -RL \\\"$1\\\" \\\"/home/"$GMIC_LOGIN"/www/CImg"${pwd}"$1\\\" ; quit\"" x $com elif isfile(['"$1"']) # Upload single file if !$is_pwd pwd="/" fi e[] "Upload file '$1' to 'SERVER"${pwd}"'.\n" - com="lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"put -O \\\"/www/CImg"${pwd}"\\\" \\\"$1\\\"; quit\"" + com="lftp sftp://"$GMIC_LOGIN":@ovh -e \"put -O \\\"/home/"$GMIC_LOGIN"/www/CImg"${pwd}"\\\" \\\"$1\\\"; quit\"" x $com fi +rm # Local Variables: # mode: sh