diff --git a/stim/envi/bsq.h b/stim/envi/bsq.h index 6c2b240..8caff0a 100644 --- a/stim/envi/bsq.h +++ b/stim/envi/bsq.h @@ -26,7 +26,7 @@ class bsq: public binary { protected: - + std::vector w; //band wavelengths unsigned int offset; @@ -51,7 +51,7 @@ public: using binary::read_line_01; using binary::read_plane_2; //using binary::getSlice; - + /// Open a data file for reading using the class interface. @@ -60,7 +60,7 @@ public: /// @param Y is the number of samples (lines) along dimension 2 /// @param B is the number of samples (bands) along dimension 3 /// @param header_offset is the number of bytes (if any) in the binary header - /// @param wavelengths is an optional STL vector of size B specifying a numerical label for each band + /// @param wavelengths is an STL vector of size B specifying a numerical label for each band bool open(std::string filename, unsigned int X, unsigned int Y, unsigned int B, unsigned int header_offset, std::vector wavelengths){ //copy the wavelengths to the BSQ file structure @@ -69,7 +69,6 @@ public: offset = header_offset; return open(filename, vec(X, Y, B), header_offset); - } /// Retrieve a single band (based on index) and stores it in pre-allocated memory. @@ -91,7 +90,7 @@ public: return band_index(p, (unsigned int)wavelength); unsigned int XY = X() * Y(); //calculate the number of pixels in a band - unsigned page = 0; + unsigned page = 0; //get the two neighboring bands (above and below 'wavelength') @@ -165,13 +164,13 @@ public: file.seekg((bandnum - 1) * sizeof(T), std::ios::cur); //go to the next band } - return true; + return true; } /// Perform baseline correction given a list of baseline points and stores the result in a new BSQ file. /// @param outname is the name of the output file used to store the resulting baseline-corrected data. - /// @param wls is the list of baseline points based on band labels. + /// @param wls is the list of baseline points based on band labels. bool baseline(std::string outname, std::vector wls ) { unsigned N = wls.size(); //get the number of baseline points @@ -194,8 +193,8 @@ public: T * c; //pointer to the current image a = (T*)malloc( S ); //memory allocation - b = (T*)malloc( S ); - c = (T*)malloc( S ); + b = (T*)malloc( S ); + c = (T*)malloc( S ); if (a == NULL || b == NULL || c == NULL){ std::cout<<"ERROR: error allocating memory"; @@ -203,13 +202,13 @@ public: } - //initialize lownum, highnum, low, high + //initialize lownum, highnum, low, high ai=w[0]; //if no baseline point is specified at band 0, //set the baseline point at band 0 to 0 if(wls[0] != w[0]){ - bi = wls[control]; + bi = wls[control]; memset(a, (char)0, S); } //else get the low band @@ -221,7 +220,7 @@ public: //get the high band band(b, bi); - //correct every band + //correct every band for(unsigned cii = 0; cii < B; cii++){ //update baseline points, if necessary @@ -253,21 +252,21 @@ public: //get the current band band_index(c, cii); ci = w[cii]; - + //perform the baseline correction for(unsigned i=0; i < XY; i++){ double r = (double) (ci - ai) / (double) (bi - ai); c[i] =(T) ( c[i] - (b[i] - a[i]) * r - a[i] ); } - + target.write(reinterpret_cast(c), S); //write the corrected data into destination thread_data = (double)cii / B * 100; - - } + + } //header.save(headername); //save the new header file - + free(a); free(b); free(c); @@ -295,7 +294,7 @@ public: T * c; //pointer to the current image b = (T*)malloc( S ); //memory allocation - c = (T*)malloc( S ); + c = (T*)malloc( S ); band(b, w); //get the certain band into memory @@ -314,17 +313,17 @@ public: thread_data = (double)j / B * 100; } - + //header.save(headername); //save the new header file - + free(b); free(c); target.close(); thread_data = 100; //make sure that the progress bar is full return true; } - + /// Convert the current BSQ file to a BIP file with the specified file name. /// @param outname is the name of the output BIP file to be saved to disk. @@ -356,10 +355,10 @@ public: //simplify image resolution unsigned int L = X() * Z() * sizeof(T); //calculate the number of bytes of a ZX slice unsigned int jump = (Y() - 1) * X() * sizeof(T); - + std::ofstream target(outname.c_str(), std::ios::binary); std::string headername = outname + ".hdr"; - + unsigned int xz_bytes = X() * Z() * sizeof(T); T * xz_slice; //pointer to the current spectrum xz_slice = (T*)malloc(xz_bytes); @@ -372,16 +371,16 @@ public: file.read((char *)(xz_slice + z * X()), sizeof(T) * X()); //read a line file.seekg(jump, std::ios::cur); //seek to the next band - + thread_data = (double)(y * Z() + z) / (Z() * Y()) * 100; //update the progress counter - } - target.write(reinterpret_cast(xz_slice), xz_bytes); //write the generated XZ slice to the target file + } + target.write(reinterpret_cast(xz_slice), xz_bytes); //write the generated XZ slice to the target file } - + thread_data = 100; - + free(xz_slice); target.close(); @@ -425,7 +424,7 @@ public: rp = (T*) malloc(S); band(lp, lb); - band(rp, rb); + band(rp, rb); baseline_band(lb, rb, lp, rp, bandwavelength, result); @@ -496,7 +495,7 @@ public: } baseline_band(lb, rb, lp, rp, lab, cur2); //beginnning part baseline_band(lb, rb, lp, rp, w[ai], cur); - for(unsigned j = 0; j < XY; j++){ + for(unsigned j = 0; j < XY; j++){ result[j] += (w[ai] - lab) * (cur[j] + cur2[j]) / 2.0; } @@ -507,7 +506,7 @@ public: baseline_band(lb, rb, lp, rp, w[ai], cur2); for(unsigned j = 0; j < XY; j++) { - result[j] += (w[ai] - w[ai-1]) * (cur[j] + cur2[j]) / 2.0; + result[j] += (w[ai] - w[ai-1]) * (cur[j] + cur2[j]) / 2.0; } std::swap(cur,cur2); //swap the band pointers } @@ -530,9 +529,9 @@ public: /// @param result is a pointer to a pre-allocated array at least X * Y * sizeof(T) in size bool ph_to_ph(double lb1, double rb1, double pos1, double lb2, double rb2, double pos2, T * result){ - T* p1 = (T*)malloc(X() * Y() * sizeof(T)); - T* p2 = (T*)malloc(X() * Y() * sizeof(T)); - + T* p1 = (T*)malloc(X() * Y() * sizeof(T)); + T* p2 = (T*)malloc(X() * Y() * sizeof(T)); + //get the two peak band height(lb1, rb1, pos1, p1); height(lb2, rb2, pos2, p2); @@ -546,9 +545,9 @@ public: free(p1); free(p2); - return true; + return true; } - + /// Compute the ratio between a peak area and peak height. /// @param lb1 is the label value for the left baseline point for the first peak (numerator) @@ -560,10 +559,10 @@ public: /// @param result is a pointer to a pre-allocated array at least X * Y * sizeof(T) in size bool pa_to_ph(double lb1, double rb1, double lab1, double rab1, double lb2, double rb2, double pos, T* result){ - - T* p1 = (T*)malloc(X() * Y() * sizeof(T)); - T* p2 = (T*)malloc(X() * Y() * sizeof(T)); - + + T* p1 = (T*)malloc(X() * Y() * sizeof(T)); + T* p2 = (T*)malloc(X() * Y() * sizeof(T)); + //get the area and the peak band area(lb1, rb1, lab1, rab1, p1); height(lb2, rb2, pos, p2); @@ -577,9 +576,9 @@ public: free(p1); free(p2); - return true; - } - + return true; + } + /// Compute the ratio between two peak areas. /// @param lb1 is the label value for the left baseline point for the first peak (numerator) @@ -589,14 +588,14 @@ public: /// @param lb2 is the label value for the left baseline point for the second peak (denominator) /// @param rb2 is the label value for the right baseline point for the second peak (denominator) /// @param lab2 is the label value for the left bound (start of the integration) of the second peak (denominator) - /// @param rab2 is the label value for the right bound (end of the integration) of the second peak (denominator) + /// @param rab2 is the label value for the right bound (end of the integration) of the second peak (denominator) /// @param result is a pointer to a pre-allocated array at least X * Y * sizeof(T) in size bool pa_to_pa(double lb1, double rb1, double lab1, double rab1, double lb2, double rb2, double lab2, double rab2, T* result){ - - T* p1 = (T*)malloc(X() * Y() * sizeof(T)); - T* p2 = (T*)malloc(X() * Y() * sizeof(T)); - + + T* p1 = (T*)malloc(X() * Y() * sizeof(T)); + T* p2 = (T*)malloc(X() * Y() * sizeof(T)); + //get the area and the peak band area(lb1, rb1, lab1, rab1, p1); area(lb2, rb2, lab2, rab2, p2); @@ -610,8 +609,8 @@ public: free(p1); free(p2); - return true; - } + return true; + } /// Compute the definite integral of a baseline corrected peak. @@ -671,7 +670,7 @@ public: } baseline_band(lb, rb, lp, rp, lab, cur2); //beginnning part baseline_band(lb, rb, lp, rp, w[ai], cur); - for(unsigned j = 0; j < XY; j++){ + for(unsigned j = 0; j < XY; j++){ result[j] += (w[ai] - lab) * (w[ai] + lab) * (cur[j] + cur2[j]) / 4.0; } @@ -682,7 +681,7 @@ public: baseline_band(lb, rb, lp, rp, w[ai], cur2); for(unsigned j = 0; j < XY; j++) { - result[j] += (w[ai] - w[ai-1]) * (w[ai] + w[ai-1]) * (cur[j] + cur2[j]) / 4.0; + result[j] += (w[ai] - w[ai-1]) * (w[ai] + w[ai-1]) * (cur[j] + cur2[j]) / 4.0; } std::swap(cur,cur2); //swap the band pointers } @@ -702,9 +701,9 @@ public: /// @param rab is the label for the end of the peak /// @param result is a pointer to a pre-allocated array at least X * Y * sizeof(T) in size bool cpoint(double lb, double rb, double lab, double rab, T* result){ - T* p1 = (T*)malloc(X() * Y() * sizeof(T)); - T* p2 = (T*)malloc(X() * Y() * sizeof(T)); - + T* p1 = (T*)malloc(X() * Y() * sizeof(T)); + T* p2 = (T*)malloc(X() * Y() * sizeof(T)); + //get the area and the peak band x_area(lb, rb, lab, rab, p1); area(lb, rb, lab, rab, p2); @@ -718,7 +717,7 @@ public: free(p1); free(p2); - return true; + return true; } /// Create a mask based on a given band and threshold value. @@ -777,7 +776,80 @@ public: return true; } + /// Saves only those pixels in a band corresponding to the mask value != 0 + /// @param array is a pointer to the destination array used to store the data + /// @param i is the band index + /// @p is a pointer to the mask data + bool sift_band(T* values, unsigned long b, unsigned char* p){ + unsigned long long XY = X() * Y(); //Number of XY pixels + unsigned long long L = XY * sizeof(T); //size of XY plane (in bytes) + + unsigned long long vi = 0; //create an index into the values array + + unsigned long long start, end; //start and end indices of a block of masked pixels in the band + bool reading = false; + unsigned long long l, i; //length of the masked region + + for(unsigned long long xy = 0; xy < XY; xy++){ //for each pixel in the band + + if(reading){ //if we are currently iterating through a block of masked pixels + if(p[xy]) end++; //if the current pixel is part of that block, increment the end index + else{ //otherwise, terminate the block + reading = false; //stop reading + i = b * XY + start; + file.seekg( i * sizeof(T) + header); //skip all of the unmasked pixels up to this point + l = (end - start); + file.read((char *)&values[vi], l * sizeof(T)); //write the values between start and end to the values array + vi += l; //increment the pointer into the values array + } + } + else{ //if we are currently iterating through a block of empty pixels + if(p[xy]){ //if the next pixel is masked, start a new block for reading + reading = true; + start = xy; //set the start and end indices to the beginning of this new block + end = start + 1; + } + } + } + + return true; + } + + /// Saves only those spectra corresponding to mask value != 0 to pre-allocated memory + /// @param matrix is the destination for the sifted pixels + /// @param p is the mask file used for sifting + bool sift(T* matrix, unsigned char* p){ + + // open a band (XY plane) + unsigned long XY = X() * Y(); //Number of XY pixels + unsigned long L = XY * sizeof(T); //size of XY pixels + + //count the number of masked pixels + unsigned long P = 0; //allocate space for the number of pixels + for(unsigned long xy = 0; xy < XY; xy++){ //count the number of pixels + if(p[xy] != 0) P++; + } + + T* bandvals = (T*) malloc(sizeof(T) * P); //allocate a temporary array to store the pixels for a single band + memset(matrix, 0, sizeof(T) * P * Z()); + for (unsigned long z = 0; z < Z(); z++){ //for each band + + if(!sift_band(bandvals, z, p)){ + std::cout<<"ERROR sifting band index "<(temp), L); //write slice data into target file + out.write(reinterpret_cast(temp), L); //write slice data into target file file.seekg(jumpb, std::ios::cur); } free(temp); @@ -1030,4 +1102,4 @@ public: #endif - + diff --git a/stim/envi/envi.h b/stim/envi/envi.h index 03ee5b3..fc7345e 100644 --- a/stim/envi/envi.h +++ b/stim/envi/envi.h @@ -465,7 +465,26 @@ public: return false; } + /// sift copies all spectra corresponding to nonzero values of a mask into a pre-allocated matrix + /// @param matrix is the destination for the pixel data + /// @param p is the mask + bool sift(void* matrix, unsigned char* p){ + + if (header.interleave == envi_header::BSQ){ //if the infile is bsq file + if (header.data_type == envi_header::float32) + return ((bsq*)file)->sift((float*)matrix, p); + else if (header.data_type == envi_header::float64) + return ((bsq*)file)->sift((double*)matrix, p); + else + std::cout << "ERROR: unidentified data type" << std::endl; + } + return false; + + } + /// sift-mask saves in an array only those spectra corresponding to nonzero values of the mask. + /// @param outfile is the name of the sifted output file + /// @param p is the mask bool sift(std::string outfile, unsigned char* p) { diff --git a/stim/image/CImg.h b/stim/image/CImg.h index 06cb44e..265a9b7 100644 --- a/stim/image/CImg.h +++ b/stim/image/CImg.h @@ -54,7 +54,7 @@ // Set version number of the library. #ifndef cimg_version -#define cimg_version 165 +#define cimg_version 169 /*----------------------------------------------------------- # @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -122,16 +123,30 @@ #pragma warning(disable:4820) #pragma warning(disable:4996) #define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_WARNINGS 1 #define _CRT_NONSTDC_NO_DEPRECATE 1 -#define cimg_snprintf cimg::c99_snprintf -#define cimg_vsnprintf cimg::c99_vsnprintf #endif -#ifndef cimg_snprintf +// Define correct string functions for each compiler and OS. +#if cimg_OS==2 && defined(_MSC_VER) +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else #include +#if defined(__MACOSX__) || defined(__APPLE__) +#define cimg_sscanf cimg::_sscanf +#define cimg_sprintf cimg::_sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf #define cimg_snprintf snprintf #define cimg_vsnprintf vsnprintf #endif +#endif // Include OS-specific headers. #if cimg_OS==1 @@ -145,6 +160,9 @@ #ifndef NOMINMAX #define NOMINMAX #endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif #include #ifndef _WIN32_IE #define _WIN32_IE 0x0400 @@ -154,15 +172,28 @@ #include #endif -// Look for C++11 features +// Look for C++11 features. #if !defined(cimg_use_cpp11) && __cplusplus>201100 -#define cimg_use_cpp11 +#define cimg_use_cpp11 1 #endif -#ifdef cimg_use_cpp11 +#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 #include #include #endif +// Configure the 'abort' signal handler (does nothing by default). +// A typical signal handler can be defined in your own source like this: +// Without OpenMP support: #define cimg_test_abort() if (is_abort) throw CImgAbortException("") +// +// or +// +// With OpenMP support: #define cimg_test_abort() if (!omp_get_thread_num() && is_abort) throw CImgAbortException("") +// +// where 'is_abort' is a boolean variable. +#ifndef cimg_test_abort +#define cimg_test_abort() +#endif + // Configure filename separator. // // Filename separator is set by default to '/', except for Windows where it is '\'. @@ -186,7 +217,11 @@ // // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals. #ifndef cimg_verbosity +#if cimg_OS==2 #define cimg_verbosity 2 +#else +#define cimg_verbosity 1 +#endif #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4) #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined. #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }). @@ -201,11 +236,7 @@ #if cimg_OS==0 #define cimg_display 0 #elif cimg_OS==1 -#if defined(__MACOSX__) || defined(__APPLE__) -#define cimg_display 1 -#else #define cimg_display 1 -#endif #elif cimg_OS==2 #define cimg_display 2 #endif @@ -471,26 +502,6 @@ extern "C" { #define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false) #define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0) #define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage) -#define cimg_argument(pos) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv) -#define cimg_argument1(pos,s0) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,1,s0) -#define cimg_argument2(pos,s0,s1) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,2,s0,s1) -#define cimg_argument3(pos,s0,s1,s2) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,3,s0,s1,s2) -#define cimg_argument4(pos,s0,s1,s2,s3) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3) -#define cimg_argument5(pos,s0,s1,s2,s3,s4) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4) -#define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5) -#define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6) -#define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7) -#define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) \ - cimg_library_suffixed::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8) // Macros to define and manipulate local neighborhoods. #define CImg_2x2(I,T) T I[4]; \ @@ -2069,26 +2080,26 @@ namespace cimg_library_suffixed { // Declare cimg:: namespace. // This is an uncomplete namespace definition here. It only contains some - // necessary stuffs to ensure a correct declaration order of the classes and functions + // necessary stuff to ensure a correct declaration order of the classes and functions // defined afterwards. namespace cimg { // Define ascii sequences for colored terminal output. #ifdef cimg_use_vt100 - const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; - const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; - const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; - const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; - const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; - const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; - const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; - const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; - const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; - const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; - const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; + static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; + static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; + static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; + static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; + static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; + static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; + static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; + static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; + static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; + static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; + static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; #else - const char t_normal[] = { 0 }; - const char *const t_black = cimg::t_normal, + static const char t_normal[] = { 0 }; + static const char *const t_black = cimg::t_normal, *const t_red = cimg::t_normal, *const t_green = cimg::t_normal, *const t_yellow = cimg::t_normal, @@ -2114,30 +2125,68 @@ namespace cimg_library_suffixed { inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) { static unsigned int mode = cimg_verbosity; - cimg::mutex(0); - if (is_set) mode = value; - cimg::mutex(0,0); + if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } return mode; } // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character // at the end of the string. #if cimg_OS==2 && defined(_MSC_VER) - inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) { - int count = -1; + inline int _snprintf(char *const s, const size_t size, const char *const format, ...) { + va_list ap; + va_start(ap,format); + const int result = _vsnprintf(s,size,format,ap); + va_end(ap); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) { + int result = -1; cimg::mutex(6); - if (size) count = _vsnprintf_s(str,size,_TRUNCATE,format,ap); - if (count==-1) count = _vscprintf(format,ap); + if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap); + if (result==-1) result = _vscprintf(format,ap); cimg::mutex(6,0); - return count; + return result; } - inline int c99_snprintf(char* str, size_t size, const char* format, ...) { - int count; - va_list ap; - va_start(ap, format); - count = c99_vsnprintf(str,size,format,ap); - va_end(ap); - return count; + + // Mutex-protected version of sscanf, sprintf and snprintf. + // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX. +#elif defined(__MACOSX__) || defined(__APPLE__) + inline int _sscanf(const char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsscanf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _sprintf(char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsprintf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _snprintf(char *const s, const size_t n, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsnprintf(s,n,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) { + cimg::mutex(6); + const int result = std::vsnprintf(s,size,format,ap); + cimg::mutex(6,0); + return result; } #endif @@ -2163,6 +2212,31 @@ namespace cimg_library_suffixed { return _exception_mode(0,false); } + //! Set current \CImg openmp mode. + /** + The way openmp-based methods are handled by \CImg can be changed dynamically, using this function. + \param mode Desired openmp mode. Possible values are: + - \c 0: Never parallelize (quiet mode). + - \c 1: Always parallelize. + - \c 2: Adaptive parallelization mode (default behavior). + **/ + inline unsigned int& _openmp_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = 2; + if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); } + return mode; + } + + inline unsigned int& openmp_mode(const unsigned int mode) { + return _openmp_mode(mode,true); + } + + //! Return current \CImg openmp mode. + inline unsigned int& openmp_mode() { + return _openmp_mode(0,false); + } + +#define cimg_openmp_if(cond) if (cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond))) + // Display a simple dialog box, and wait for the user's response. inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK", const char *const button2_label=0, const char *const button3_label=0, @@ -2172,6 +2246,7 @@ namespace cimg_library_suffixed { // Evaluate math expression. inline double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0); + } /*--------------------------------------- @@ -2183,9 +2258,12 @@ namespace cimg_library_suffixed { /** \par Overview - CImgException is the base class of all exceptions thrown by \CImg. + CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException). CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead. - These derived classes can be: + These classes can be: + + - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal. + This is the only \c non-derived exception class. - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid. This is probably one of the most thrown exception by \CImg. @@ -2243,28 +2321,65 @@ namespace cimg_library_suffixed { **/ struct CImgException : public std::exception { #define _cimg_exception_err(etype,disp_flag) \ - std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,16384,format,ap); va_end(ap); \ - if (cimg::exception_mode()) { \ - std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \ - if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \ - catch (CImgException&) {} \ - if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \ - } + std::va_list ap, ap2; \ + va_start(ap,format); va_start(ap2,format); \ + int size = cimg_vsnprintf(0,0,format,ap2); \ + if (size++>=0) { \ + delete[] _message; \ + _message = new char[size]; \ + cimg_vsnprintf(_message,size,format,ap); \ + if (cimg::exception_mode()) { \ + std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \ + if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \ + catch (CImgException&) {} \ + if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \ + } \ + } \ + va_end(ap); va_end(ap2); \ char *_message; - CImgException() { _message = new char[16384]; *_message = 0; } - CImgException(const char *const format, ...) { - _message = new char[16384]; *_message = 0; _cimg_exception_err("CImgException",true); + CImgException() { _message = new char[1]; *_message = 0; } + CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); } + CImgException(const CImgException& e) { + const int size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; } ~CImgException() throw() { delete[] _message; } + CImgException& operator=(const CImgException& e) { + const int size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } //! Return a C-string containing the error message associated to the thrown exception. const char *what() const throw() { return _message; } }; - // The CImgInstanceException class is used to throw an exception related - // to an invalid instance encountered in a library function call. - struct CImgInstanceException : public CImgException { - CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } + // The CImgAbortException class is used to throw an exception when + // a computationally-intensive function has been aborted by an external signal. + struct CImgAbortException : public std::exception { + char *_message; + CImgAbortException() { _message = new char[1]; *_message = 0; } + CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); } + CImgAbortException(const CImgAbortException& e) { + const int size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + } + ~CImgAbortException() throw() { delete[] _message; } + CImgAbortException& operator=(const CImgAbortException& e) { + const int size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } }; // The CImgArgumentException class is used to throw an exception related @@ -2273,18 +2388,24 @@ namespace cimg_library_suffixed { CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); } }; - // The CImgIOException class is used to throw an exception related - // to input/output file problems encountered in a library function call. - struct CImgIOException : public CImgException { - CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); } - }; - // The CImgDisplayException class is used to throw an exception related // to display problems encountered in a library function call. struct CImgDisplayException : public CImgException { CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); } }; + // The CImgInstanceException class is used to throw an exception related + // to an invalid instance encountered in a library function call. + struct CImgInstanceException : public CImgException { + CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } + }; + + // The CImgIOException class is used to throw an exception related + // to input/output file problems encountered in a library function call. + struct CImgIOException : public CImgException { + CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); } + }; + // The CImgWarningException class is used to throw an exception for warnings // encountered in a library function call. struct CImgWarningException : public CImgException { @@ -2497,10 +2618,22 @@ namespace cimg_library_suffixed { return !(val==val); #endif } - static double min() { return -1.7E308; } - static double max() { return 1.7E308; } - static double inf() { return max()*max(); } - static double nan() { const double val_nan = -std::sqrt(-1.0); return val_nan; } + static double min() { return -DBL_MAX; } + static double max() { return DBL_MAX; } + static double inf() { +#ifdef INFINITY + return (double)INFINITY; +#else + return max()*max(); +#endif + } + static double nan() { +#ifdef NAN + return (double)NAN; +#else + const double val_nan = -std::sqrt(-1.0); return val_nan; +#endif + } static double cut(const double val) { return valmax()?max():val; } static const char* format() { return "%.16g"; } static double format(const double val) { return val; } @@ -2523,8 +2656,8 @@ namespace cimg_library_suffixed { return !(val==val); #endif } - static float min() { return -3.4E38f; } - static float max() { return 3.4E38f; } + static float min() { return -FLT_MAX; } + static float max() { return FLT_MAX; } static float inf() { return (float)cimg::type::inf(); } static float nan() { return (float)cimg::type::nan(); } static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; } @@ -2532,6 +2665,32 @@ namespace cimg_library_suffixed { static double format(const float val) { return (double)val; } }; + template<> struct type { + static const char* string() { static const char *const s = "long double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const long double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const long double val) { +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static long double min() { return -LDBL_MAX; } + static long double max() { return LDBL_MAX; } + static long double inf() { return max()*max(); } + static long double nan() { const long double val_nan = -std::sqrt(-1.0L); return val_nan; } + static long double cut(const long double val) { return valmax()?max():val; } + static const char* format() { return "%.16g"; } + static double format(const long double val) { return (double)val; } + }; + template struct superset { typedef T type; }; template<> struct superset { typedef unsigned char type; }; template<> struct superset { typedef char type; }; @@ -3015,7 +3174,7 @@ namespace cimg_library_suffixed { const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI // Define a 12x13 font (small size). - const char *const data_font12x13 = + static const char *const data_font12x13 = " .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w Fw mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxl" "wlwkwuwnwuynwuwTwlwlwtwnwtwnw my Qw +wlw b{ \\w Wx`xTw_w[wbxawSwkw nynwky])]2_4^U^ 6^T\\5])]1_2]T\\8^U^ K])]2`4^V^3] " " U]*\\2a4`V\\8^U^5a F]*\\1\\X\\4^U^=]*\\" @@ -3316,7 +3475,7 @@ namespace cimg_library_suffixed { " F]']2] +]']2^ D]']3_ E]']1] \"]']2^ 8] H"; // Define a 90x103 font (huge size). - const char *const _data_font90x103[] = { // Defined as an array to avoid MS compiler limit about constant string (65Kb). + static const char *const _data_font90x103[] = { // Defined as an array to avoid MS compiler limit about constant string (65Kb). // Start of first string. " " " " @@ -3856,7 +4015,7 @@ namespace cimg_library_suffixed { " D" }; // Define a 40x38 'danger' color logo (used by cimg::dialog()). - const unsigned char logo40x38[4576] = { + static const unsigned char logo40x38[4576] = { 177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200, 1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0, 0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200, @@ -3882,23 +4041,6 @@ namespace cimg_library_suffixed { 0,4,123,123,123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25, 123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 }; - // Mutex-protected version of sscanf. - // Used only MacOSX, as it seems std::sscanf() is not re-entrant on MacOSX. -#if (__MACOSX__) || defined(__APPLE__) -#define cimg_sscanf cimg::_sscanf - inline int _sscanf(const char *s, const char *format, ...) { - cimg::mutex(13); - va_list args; - va_start(args, format); - const int result = std::vsscanf(s,format,args); - va_end(args); - cimg::mutex(13,0); - return result; - } -#else -#define cimg_sscanf std::sscanf -#endif - //! Get/set default output stream for the \CImg library messages. /** \param file Desired output stream. Set to \c 0 to get the currently used output stream only. @@ -3980,7 +4122,7 @@ namespace cimg_library_suffixed { return -1; #else #if cimg_OS==1 - const unsigned int l = std::strlen(command); + const unsigned int l = (unsigned int)std::strlen(command); if (l) { char *const ncommand = new char[l + 16]; std::strncpy(ncommand,command,l); @@ -4243,8 +4385,9 @@ namespace cimg_library_suffixed { _rand(seed,true); } - inline double rand() { - return cimg::_rand()/16777215.; + inline double rand(const double val_min, const double val_max) { + const double val = cimg::_rand()/16777215.; + return val_min + (val_max - val_min)*val; } #else @@ -4265,19 +4408,20 @@ namespace cimg_library_suffixed { std::srand(seed); } - //! Return a random variable between [0,1] with respect to an uniform distribution. + //! Return a random variable uniformely distributed between [val_min,val_max]. /** **/ - inline double rand() { - return (double)std::rand()/RAND_MAX; + inline double rand(const double val_min, const double val_max) { + const double val = (double)std::rand()/RAND_MAX; + return val_min + (val_max - val_min)*val; } #endif - //! Return a random variable between [-1,1] with respect to an uniform distribution. + //! Return a random variable uniformely distributed between [0,val_max]. /** - **/ - inline double crand() { - return 1 - 2*cimg::rand(); + **/ + inline double rand(const double val_max=1) { + return cimg::rand(0,val_max); } //! Return a random variable following a gaussian distribution and a standard deviation of 1. @@ -4286,8 +4430,8 @@ namespace cimg_library_suffixed { inline double grand() { double x1, w; do { - const double x2 = 2*cimg::rand() - 1.0; - x1 = 2*cimg::rand() - 1.0; + const double x2 = cimg::rand(-1,1); + x1 = cimg::rand(-1,1); w = x1*x1 + x2*x2; } while (w<=0 || w>=1.0); return x1*std::sqrt((-2*std::log(w))/w); @@ -4319,6 +4463,10 @@ namespace cimg_library_suffixed { return (double)rol((long)a,n); } + inline double rol(const long double a, const unsigned int n=1) { + return (double)rol((long)a,n); + } + //! Bitwise-rotate value on the right. template inline T ror(const T& a, const unsigned int n=1) { @@ -4333,6 +4481,10 @@ namespace cimg_library_suffixed { return (double)ror((long)a,n); } + inline double ror(const long double a, const unsigned int n=1) { + return (double)ror((long)a,n); + } + //! Return absolute value of a value. template inline T abs(const T& a) { @@ -4515,11 +4667,12 @@ namespace cimg_library_suffixed { else { const double tmp = absa/absb; return absb==0?0:absb*std::sqrt(1.0 + tmp*tmp); } } - inline bool _is_self_expr(const char *expression) { - if (!expression || *expression=='>' || *expression=='<') return false; - for (const char *s = expression; *s; ++s) - if ((*s=='i' || *s=='j') && (s[1]=='(' || s[1]=='[')) return true; - return false; + //! Return sqrt(x^2 + y^2). + inline double hypot(const double x, const double y) { + double nx = cimg::abs(x), ny = cimg::abs(y), t; + if (nx0) { t/=nx; return nx*std::sqrt(1+t*t); } + return 0; } //! Convert ascii character to lower case. @@ -4575,6 +4728,51 @@ namespace cimg_library_suffixed { return cimg::strncasecmp(str1,str2,1 + (l1='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns; + while ((*ns>='0' && *ns<='9') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns; *nd = (char)val; break; default : *nd = *(ns++); } else *nd = *(ns++); @@ -4650,20 +4848,20 @@ namespace cimg_library_suffixed { // Return string that identifies the running OS. inline const char *stros() { #if defined(linux) || defined(__linux) || defined(__linux__) - const char *const str = "Linux"; + static const char *const str = "Linux"; #elif defined(sun) || defined(__sun) - const char *const str = "Sun OS"; + static const char *const str = "Sun OS"; #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) - const char *const str = "BSD"; + static const char *const str = "BSD"; #elif defined(sgi) || defined(__sgi) - const char *const str = "Irix"; + static const char *const str = "Irix"; #elif defined(__MACOSX__) || defined(__APPLE__) - const char *const str = "Mac OS"; + static const char *const str = "Mac OS"; #elif defined(unix) || defined(__unix) || defined(__unix__) - const char *const str = "Generic Unix"; + static const char *const str = "Generic Unix"; #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ defined(WIN64) || defined(_WIN64) || defined(__WIN64__) - const char *const str = "Windows"; + static const char *const str = "Windows"; #else const char *const _str1 = std::getenv("OSTYPE"), @@ -4683,11 +4881,12 @@ namespace cimg_library_suffixed { // Return a random filename. inline const char* filenamerand() { cimg::mutex(6); - static char randomid[9] = { 0 }; + static char randomid[9]; cimg::srand(); for (unsigned int k = 0; k<8; ++k) { - const int v = (int)std::rand()%3; - randomid[k] = (char)(v==0?('0' + (std::rand()%10)):(v==1?('a' + (std::rand()%26)):('A' + (std::rand()%26)))); + const int v = (int)cimg::rand(65535)%3; + randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)): + (v==1?('a' + ((int)cimg::rand(65535)%26)):('A' + ((int)cimg::rand(65535)%26)))); } cimg::mutex(6,0); return randomid; @@ -4815,7 +5014,7 @@ namespace cimg_library_suffixed { Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } **/ inline int date(const unsigned int attr) { - int res = -1; + int res; cimg::mutex(6); #if cimg_OS==2 SYSTEMTIME st; @@ -4892,7 +5091,7 @@ namespace cimg_library_suffixed { const char *const ext = cimg::split_filename(filename,body); if (*ext) cimg_snprintf(format,1024,"%%s_%%.%ud.%%s",digits); else cimg_snprintf(format,1024,"%%s_%%.%ud",digits); - std::sprintf(str,format,body,number,ext); + cimg_sprintf(str,format,body,number,ext); delete[] format; delete[] body; return str; } @@ -4970,7 +5169,8 @@ 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 unsigned int timeout=0, const bool try_fallback=false, + const char *const referer=0); //! Return options specified on the command line. inline const char* option(const char *const name, const int argc, const char *const *const argv, @@ -5033,8 +5233,8 @@ namespace cimg_library_suffixed { const char defaut, const char *const usage=0) { const char *const s = cimg::option(name,argc,argv,(char*)0); const char res = s?*s:defaut; - char tmp[8] = { 0 }; - *tmp = res; + char tmp[8]; + *tmp = res; tmp[1] = 0; cimg::option(name,0,0,tmp,usage); return res; } @@ -5061,25 +5261,6 @@ namespace cimg_library_suffixed { return res; } - inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv, - const unsigned int nb_singles=0, ...) { - for (int k = 1, pos = 0; k inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg& img) { @@ -5380,7 +5562,7 @@ namespace cimg_library_suffixed { template inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg& img) { - return CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum,expression,true)-=img; + return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img; } template @@ -5410,12 +5592,12 @@ namespace cimg_library_suffixed { template inline bool operator==(const char *const expression, const CImg& img) { - return img == expression; + return img==expression; } template inline bool operator!=(const char *const expression, const CImg& img) { - return img != expression; + return img!=expression; } template @@ -5528,7 +5710,7 @@ namespace cimg_library_suffixed { # Define the CImgDisplay structure # ----------------------------------*/ - //! Allow to create windows, display images on them and manage user events (keyboard, mouse and windows events). + //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events). /** CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems). @@ -5802,6 +5984,12 @@ namespace cimg_library_suffixed { return _empty.assign(); } + //! Return a reference to an empty display \const. + static const CImgDisplay& const_empty() { + static const CImgDisplay _empty; + return _empty; + } + #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false), \ CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true) static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz, @@ -6191,7 +6379,7 @@ namespace cimg_library_suffixed { in fullscreen mode. **/ const char *title() const { - return _title; + return _title?_title:""; } //! Return width of the associated window. @@ -7136,7 +7324,7 @@ namespace cimg_library_suffixed { XEvent event; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); - if (!arg) for (;;) { + if (!arg) for ( ; ; ) { cimg_lock_display(); bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event); if (!event_flag) event_flag = XCheckMaskEvent(dpy, @@ -7397,7 +7585,7 @@ namespace cimg_library_suffixed { // Allocate space for window title const char *const nptitle = ptitle?ptitle:""; - const unsigned int s = std::strlen(nptitle) + 1; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; char *const tmp_title = s?new char[s]:0; if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); @@ -7535,7 +7723,7 @@ namespace cimg_library_suffixed { // Remove display window from event thread list. unsigned int i; for (i = 0; i0)?nheight:(-nheight*height()/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; - cimg_lock_display(); - if (_window_width!=dimx || _window_height!=dimy) { - XWindowAttributes attr; - for (unsigned int i = 0; i<10; ++i) { - XResizeWindow(dpy,_window,dimx,dimy); - XGetWindowAttributes(dpy,_window,&attr); - if (attr.width==(int)dimx && attr.height==(int)dimy) break; - cimg::wait(5); + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + show(); + cimg_lock_display(); + if (_window_width!=dimx || _window_height!=dimy) { + XWindowAttributes attr; + for (unsigned int i = 0; i<10; ++i) { + XResizeWindow(dpy,_window,dimx,dimy); + XGetWindowAttributes(dpy,_window,&attr); + if (attr.width==(int)dimx && attr.height==(int)dimy) break; + cimg::wait(5); + } } + if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) { + case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } + } + _window_width = _width = dimx; _window_height = _height = dimy; + cimg_unlock_display(); } - if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) { - case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; - case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; - default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } - } - _window_width = _width = dimx; _window_height = _height = dimy; _is_resized = false; - cimg_unlock_display(); if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2); if (force_redraw) return paint(); return *this; @@ -7692,13 +7883,15 @@ namespace cimg_library_suffixed { CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; - show(); - Display *const dpy = cimg::X11_attr().display; - cimg_lock_display(); - XMoveWindow(dpy,_window,posx,posy); - _window_x = posx; _window_y = posy; + if (_window_x!=posx || _window_y!=posy) { + show(); + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XMoveWindow(dpy,_window,posx,posy); + _window_x = posx; _window_y = posy; + cimg_unlock_display(); + } _is_moved = false; - cimg_unlock_display(); return paint(); } @@ -7715,7 +7908,7 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; Display *const dpy = cimg::X11_attr().display; cimg_lock_display(); - const char pix_data[8] = { 0 }; + static const char pix_data[8] = { 0 }; XColor col; col.red = col.green = col.blue = 0; Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8); @@ -7747,7 +7940,7 @@ namespace cimg_library_suffixed { va_end(ap); if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } delete[] _title; - const unsigned int s = std::strlen(tmp) + 1; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; _title = new char[s]; std::memcpy(_title,tmp,s*sizeof(char)); Display *const dpy = cimg::X11_attr().display; @@ -8269,7 +8462,9 @@ namespace cimg_library_suffixed { } break; case WM_PAINT : disp->paint(); + cimg::mutex(15); if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); + cimg::mutex(15,0); break; case WM_ERASEBKGND : // return 0; @@ -8299,12 +8494,16 @@ namespace cimg_library_suffixed { disp->_mouse_x = disp->_mouse_y = -1; disp->_is_event = true; SetEvent(cimg::Win32_attr().wait_event); + cimg::mutex(15); if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0); + cimg::mutex(15,0); } break; case WM_MOUSELEAVE : { disp->_mouse_x = disp->_mouse_y = -1; disp->_is_mouse_tracked = false; + cimg::mutex(15); while (ShowCursor(TRUE)<0); + cimg::mutex(15,0); } break; case WM_LBUTTONDOWN : disp->set_button(1); @@ -8563,24 +8762,27 @@ namespace cimg_library_suffixed { tmpdimy = (nheight>0)?nheight:(-nheight*_height/100), dimx = tmpdimx?tmpdimx:1, dimy = tmpdimy?tmpdimy:1; - if (_window_width!=dimx || _window_height!=dimy) { - RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1; - SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); - } - if (_width!=dimx || _height!=dimy) { - unsigned int *const ndata = new unsigned int[dimx*dimy]; - if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy); - else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); - delete[] _data; - _data = ndata; - _bmi.bmiHeader.biWidth = (LONG)dimx; - _bmi.bmiHeader.biHeight = -(int)dimy; - _width = dimx; - _height = dimy; + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + if (_window_width!=dimx || _window_height!=dimy) { + RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1; + SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); + } + if (_width!=dimx || _height!=dimy) { + unsigned int *const ndata = new unsigned int[dimx*dimy]; + if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy); + else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); + delete[] _data; + _data = ndata; + _bmi.bmiHeader.biWidth = (LONG)dimx; + _bmi.bmiHeader.biHeight = -(int)dimy; + _width = dimx; + _height = dimy; + } + _window_width = dimx; _window_height = dimy; + show(); } - _window_width = dimx; _window_height = dimy; _is_resized = false; if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2); if (force_redraw) return paint(); @@ -8623,19 +8825,22 @@ namespace cimg_library_suffixed { CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; - if (!_is_fullscreen) { - RECT rect; - rect.left = rect.top = 0; rect.right = (LONG)_window_width - 1; rect.bottom = (LONG)_window_height - 1; - AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); - const int - border1 = (int)((rect.right - rect.left + 1 -_width)/2), - border2 = (int)(rect.bottom - rect.top + 1 - _height - border1); - SetWindowPos(_window,0,posx - border1,posy - border2,0,0,SWP_NOSIZE | SWP_NOZORDER); - } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); - _window_x = posx; - _window_y = posy; + if (_window_x!=posx || _window_y!=posy) { + if (!_is_fullscreen) { + RECT rect; + rect.left = rect.top = 0; rect.right = (LONG)_window_width - 1; rect.bottom = (LONG)_window_height - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int + border1 = (int)((rect.right - rect.left + 1 -_width)/2), + border2 = (int)(rect.bottom - rect.top + 1 - _height - border1); + SetWindowPos(_window,0,posx - border1,posy - border2,0,0,SWP_NOSIZE | SWP_NOZORDER); + } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); + _window_x = posx; + _window_y = posy; + show(); + } _is_moved = false; - return show(); + return *this; } CImgDisplay& show_mouse() { @@ -8651,7 +8856,7 @@ namespace cimg_library_suffixed { } CImgDisplay& set_mouse(const int posx, const int posy) { - if (_is_closed || posx<0 || posy<0) return *this; + if (is_empty() || _is_closed || posx<0 || posy<0) return *this; _update_window_pos(); const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy); if (res) { _mouse_x = posx; _mouse_y = posy; } @@ -8891,10 +9096,8 @@ namespace cimg_library_suffixed { - Construct images from C-style arrays: - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer \c data_buffer (of size 256x256=65536). - - CImg img(data_buffer,256,256,1,3,false); constructs a 256x256 color image + - CImg img(data_buffer,256,256,1,3); constructs a 256x256 color image from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). - - CImg img(data_buffer,256,256,1,3,true); constructs a 256x256 color image - from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed). The complete list of constructors can be found here. @@ -8903,7 +9106,7 @@ namespace cimg_library_suffixed { The \c CImg class contains a lot of functions that operates on images. Some of the most useful are: - - operator()(): allows to access or write pixel values. + - operator()(): Read or write pixel values. - display(): displays the image in a new window. **/ template @@ -8977,6 +9180,13 @@ namespace cimg_library_suffixed { typedef typename cimg::last::type longT; typedef typename cimg::last::type floatT; typedef typename cimg::last::type doubleT; +#if cimg_OS==2 + typedef typename cimg::last::type uptrT; // Unsigned integer type that can store a pointer. + typedef typename cimg::last::type ptrT; // Signed integer type that can store a pointer. +#else + typedef typename cimg::last::type uptrT; + typedef typename cimg::last::type ptrT; +#endif //@} //--------------------------- @@ -9165,7 +9375,7 @@ namespace cimg_library_suffixed { *(ptrd++) = (T)a0; \ if (_siz--) { \ *(ptrd++) = (T)a1; \ - for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ + for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ } \ va_end(ap); \ } \ @@ -9174,7 +9384,7 @@ namespace cimg_library_suffixed { _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int); } -#ifdef cimg_use_cpp11 +#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 //! Construct image with specified size and initialize pixel values from an initializer list of integers. /** Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, @@ -9423,7 +9633,8 @@ namespace cimg_library_suffixed { cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), size_x,size_y,size_z,size_c); } - std::memcpy(_data,values,siz*sizeof(T)); } + std::memcpy(_data,values,siz*sizeof(T)); + } } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } } @@ -9629,7 +9840,7 @@ namespace cimg_library_suffixed { // Constructor and assignment operator for rvalue references (c++11). // This avoids an additional image copy for methods returning new images. Can save RAM for big images ! -#ifdef cimg_use_cpp11 +#if defined(cimg_use_cpp11) && cimg_use_cpp11!=0 CImg(CImg&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { swap(img); } @@ -9954,10 +10165,7 @@ namespace cimg_library_suffixed { \endcode **/ CImg& swap(CImg& img) { - cimg::swap(_width,img._width); - cimg::swap(_height,img._height); - cimg::swap(_depth,img._depth); - cimg::swap(_spectrum,img._spectrum); + cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum); cimg::swap(_data,img._data); cimg::swap(_is_shared,img._is_shared); return img; @@ -9977,6 +10185,12 @@ namespace cimg_library_suffixed { return _empty.assign(); } + //! Return a reference to an empty image \const. + static const CImg& const_empty() { + static const CImg _empty; + return _empty; + } + //@} //------------------------------------------ // @@ -10023,9 +10237,9 @@ namespace cimg_library_suffixed { const unsigned long off = (unsigned long)offset(x,y,z,c); if (!_data || off>=size()) { cimg::warn(_cimg_instance - "operator(): Invalid pixel request, at coordinates (%u,%u,%u,%u) [offset=%u].", + "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].", cimg_instance, - x,y,z,c,off); + (int)x,(int)y,(int)z,(int)c,off); return *_data; } else return _data[off]; @@ -10191,7 +10405,7 @@ namespace cimg_library_suffixed { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - fill(expression,true); + _fill(expression,true,true,0,0,"operator=",0); } catch (CImgException&) { cimg::exception_mode(omode); load(expression); @@ -10254,7 +10468,7 @@ namespace cimg_library_suffixed { CImg& operator+=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if(size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd + value); return *this; @@ -10269,37 +10483,7 @@ namespace cimg_library_suffixed { instead of assigning them. **/ CImg& operator+=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator+="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)(*ptrd + lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this+=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this+=(+*this)._fill(expression,true,true,0,0,"operator+=",this); } //! In-place addition operator. @@ -10345,7 +10529,7 @@ namespace cimg_library_suffixed { CImg& operator++() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if(size()>=524288) #endif cimg_rof(*this,ptrd,T) ++*ptrd; return *this; @@ -10414,7 +10598,7 @@ namespace cimg_library_suffixed { CImg& operator-=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if(size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd - value); return *this; @@ -10425,37 +10609,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a substraction instead of an addition. **/ CImg& operator-=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator-="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)(*ptrd - lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this-=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this-=(+*this)._fill(expression,true,true,0,0,"operator-=",this); } //! In-place substraction operator. @@ -10483,7 +10637,7 @@ namespace cimg_library_suffixed { CImg& operator--() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if(size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = *ptrd - (T)1; return *this; @@ -10554,7 +10708,7 @@ namespace cimg_library_suffixed { CImg& operator*=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=262144) +#pragma omp parallel for cimg_openmp_if(size()>=262144) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd * value); return *this; @@ -10565,37 +10719,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. **/ CImg& operator*=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator*="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)(*ptrd * lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - mul(CImg(_width,_height,_depth,_spectrum,expression,true)); - } - cimg::exception_mode(omode); - return *this; + return mul((+*this)._fill(expression,true,true,0,0,"operator*=",this)); } //! In-place multiplication operator. @@ -10654,7 +10778,7 @@ namespace cimg_library_suffixed { img._width,img._height,img._depth,img._spectrum,img._data); CImg<_cimg_Tt> res(img._width,_height); #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>1024 && img.size()>1024) collapse(2) +#pragma omp parallel for collapse(2) cimg_openmp_if(size()>1024 && img.size()>1024) cimg_forXY(res,i,j) { _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value; } @@ -10675,7 +10799,7 @@ namespace cimg_library_suffixed { CImg& operator/=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd / value); return *this; @@ -10686,37 +10810,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a division instead of an addition. **/ CImg& operator/=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator/="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)(*ptrd / lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - div(CImg(_width,_height,_depth,_spectrum,expression,true)); - } - cimg::exception_mode(omode); - return *this; + return div((+*this)._fill(expression,true,true,0,0,"operator/=",this)); } //! In-place division operator. @@ -10772,7 +10866,7 @@ namespace cimg_library_suffixed { CImg& operator%=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=16384) +#pragma omp parallel for cimg_openmp_if(size()>=16384) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value); return *this; @@ -10783,37 +10877,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. **/ CImg& operator%=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator%="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::mod(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this%=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this%=(+*this)._fill(expression,true,true,0,0,"operator%=",this); } //! In-place modulo operator. @@ -10871,7 +10935,7 @@ namespace cimg_library_suffixed { CImg& operator&=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value); return *this; @@ -10882,39 +10946,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. **/ CImg& operator&=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator&="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') - cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this&=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this&=(+*this)._fill(expression,true,true,0,0,"operator&=",this); } //! In-place bitwise AND operator. @@ -10972,7 +11004,7 @@ namespace cimg_library_suffixed { CImg& operator|=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value); return *this; @@ -10983,39 +11015,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. **/ CImg& operator|=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator|="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') - cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this|=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this|=(+*this)._fill(expression,true,true,0,0,"operator|=",this); } //! In-place bitwise OR operator. @@ -11075,7 +11075,7 @@ namespace cimg_library_suffixed { CImg& operator^=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value); return *this; @@ -11088,39 +11088,7 @@ namespace cimg_library_suffixed { - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. **/ CImg& operator^=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator^="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') - cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this^=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this^=(+*this)._fill(expression,true,true,0,0,"operator^=",this); } //! In-place bitwise XOR operator. @@ -11180,7 +11148,7 @@ namespace cimg_library_suffixed { CImg& operator<<=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if(size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value); return *this; @@ -11191,37 +11159,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. **/ CImg& operator<<=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator<<="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd << (int)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this<<=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this<<=(+*this)._fill(expression,true,true,0,0,"operator<<=",this); } //! In-place bitwise left shift operator. @@ -11280,7 +11218,7 @@ namespace cimg_library_suffixed { CImg& operator>>=(const t value) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if(size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value); return *this; @@ -11291,37 +11229,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. **/ CImg& operator>>=(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator<<="); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)((long)*ptrd >> (int)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - cimg::exception_mode(omode); - *this>>=CImg(_width,_height,_depth,_spectrum,expression,true); - } - cimg::exception_mode(omode); - return *this; + return *this>>=(+*this)._fill(expression,true,true,0,0,"operator>>=",this); } //! In-place bitwise right shift operator. @@ -11403,25 +11311,7 @@ namespace cimg_library_suffixed { \param expression Value string describing the way pixel values are compared. **/ bool operator==(const char *const expression) const { - if (is_empty()) return !*expression; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - bool is_equal = true; - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"operator<<="); - const T *ptrs = *expression=='<'?end() - 1:_data; - if (*expression=='<') - cimg_rofXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs--)==mp(x,y,z,c)); } - else if (*expression=='>') - cimg_forXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs++)==mp(x,y,z,c)); } - else cimg_forXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs++)==mp(x,y,z,c)); } - } catch (CImgException&) { - cimg::exception_mode(omode); - is_equal = (*this==CImg(_width,_height,_depth,_spectrum,expression,true)); - } - cimg::exception_mode(omode); - return is_equal; + return *this==(+*this)._fill(expression,true,true,0,0,"operator==",this); } //! Test if two images have the same size and values. @@ -12946,6 +12836,8 @@ namespace cimg_library_suffixed { of the image instance (written in base 10), separated by specified \c separator character. \param separator A \c char character which specifies the separator between values in the returned C-string. \param max_size Maximum size of the returned image. + \param format For float-values, tell the printf format used to generate the ascii representation of the numbers. + (or \c 0 for default representation). \note - The returned image is never empty. - For an empty image instance, the returned string is "". @@ -12953,15 +12845,18 @@ namespace cimg_library_suffixed { - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off and terminated by character \c '\0'. In that case, the returned image size is max_size + 1. **/ - CImg value_string(const char separator=',', const unsigned int max_size=0) const { + CImg value_string(const char separator=',', const unsigned int max_size=0, + const char *const format=0) const { if (is_empty()) return CImg::string(""); CImgList items; CImg s_item(256); *s_item = 0; const T *ptrs = _data; unsigned int string_size = 0; + const char *const _format = format?format:cimg::type::format(); + for (unsigned long off = 0, siz = (unsigned int)size(); off::format(),cimg::type::format(*(ptrs++))); + const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format, + cimg::type::format(*(ptrs++))); CImg item(s_item._data,printed_size); item[printed_size - 1] = separator; item.move_to(items); @@ -13011,9 +12906,9 @@ namespace cimg_library_suffixed { return false; } - //! Test if image instance contains a 'nan' value. + //! Test if image instance contains a NaN value. /** - Return \c true, if image instance contains a 'nan' value, and \c false otherwise. + Return \c true, if image instance contains a NaN value, and \c false otherwise. **/ bool is_nan() const { if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_nan((float)*p)) return true; @@ -13430,7 +13325,7 @@ namespace cimg_library_suffixed { // Check consistency for the particular case of an empty 3d object. if (is_empty()) { if (primitives || colors || opacities) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines no vertices but %u primitives, " "%u colors and %lu opacities", _width,primitives._width,primitives._width, @@ -13442,19 +13337,19 @@ namespace cimg_library_suffixed { // Check consistency of vertices. if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions. - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)", _width,primitives._width,_width,_height,_depth,_spectrum); return false; } if (colors._width>primitives._width + 1) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines %u colors", _width,primitives._width,colors._width); return false; } if (opacities.size()>primitives._width) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines %lu opacities", _width,primitives._width,(unsigned long)opacities.size()); return false; @@ -13469,7 +13364,7 @@ namespace cimg_library_suffixed { case 1 : { // Point. const unsigned int i0 = (unsigned int)primitive(0); if (i0>=_width) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) refers to invalid vertex indice %u in " "point primitive [%u]", _width,primitives._width,i0,l); @@ -13481,7 +13376,7 @@ namespace cimg_library_suffixed { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in " "sphere primitive [%u]", _width,primitives._width,i0,i1,l); @@ -13494,7 +13389,7 @@ namespace cimg_library_suffixed { i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1); if (i0>=_width || i1>=_width) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in " "segment primitive [%u]", _width,primitives._width,i0,i1,l); @@ -13508,7 +13403,7 @@ namespace cimg_library_suffixed { i1 = (unsigned int)primitive(1), i2 = (unsigned int)primitive(2); if (i0>=_width || i1>=_width || i2>=_width) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " "triangle primitive [%u]", _width,primitives._width,i0,i1,i2,l); @@ -13523,7 +13418,7 @@ namespace cimg_library_suffixed { i2 = (unsigned int)primitive(2), i3 = (unsigned int)primitive(3); if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " "quadrangle primitive [%u]", _width,primitives._width,i0,i1,i2,i3,l); @@ -13531,7 +13426,7 @@ namespace cimg_library_suffixed { } } break; default : - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines an invalid primitive [%u] of size %u", _width,primitives._width,l,(unsigned int)psiz); return false; @@ -13542,7 +13437,7 @@ namespace cimg_library_suffixed { cimglist_for(colors,c) { const CImg& color = colors[c]; if (!color) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines no color for primitive [%u]", _width,primitives._width,c); return false; @@ -13553,7 +13448,7 @@ namespace cimg_library_suffixed { if (colors._width>primitives._width) { const CImg &light = colors.back(); if (!light || light._depth>1) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "3d object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)", _width,primitives._width,light._width, light._height,light._depth,light._spectrum); @@ -13579,7 +13474,7 @@ namespace cimg_library_suffixed { // Check instance dimension and header. if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d has invalid dimensions (%u,%u,%u,%u)", _width,_height,_depth,_spectrum); return false; @@ -13587,7 +13482,7 @@ namespace cimg_library_suffixed { const T *ptrs = _data, *const ptre = end(); if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') || !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d header not found"); return false; } @@ -13599,7 +13494,7 @@ namespace cimg_library_suffixed { if (!full_check) { const unsigned long minimal_size = 8UL + 3*nb_points + 6*nb_primitives; if (_data + minimal_size>ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", nb_points,nb_primitives,size(),minimal_size); return false; @@ -13609,13 +13504,13 @@ namespace cimg_library_suffixed { // Check consistency of vertex data. if (!nb_points) { if (nb_primitives) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines no vertices but %u primitives", nb_points,nb_primitives,nb_primitives); return false; } if (ptrs!=ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) is an empty object but contains %u value%s " "more than expected", nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); @@ -13624,7 +13519,7 @@ namespace cimg_library_suffixed { return true; } if (ptrs + 3*nb_points>ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines only %u vertices data", nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); return false; @@ -13633,7 +13528,7 @@ namespace cimg_library_suffixed { // Check consistency of primitive data. if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines %u vertices but no primitive", nb_points,nb_primitives,nb_points); return false; @@ -13647,7 +13542,7 @@ namespace cimg_library_suffixed { case 1 : { // Point. const unsigned int i0 = cimg::float2uint((float)*(ptrs++)); if (i0>=nb_points) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive [%u]", nb_points,nb_primitives,i0,p); return false; @@ -13659,7 +13554,7 @@ namespace cimg_library_suffixed { i1 = cimg::float2uint((float)*(ptrs++)); ptrs+=3; if (i0>=nb_points || i1>=nb_points) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " "sphere primitive [%u]", nb_points,nb_primitives,i0,i1,p); @@ -13672,7 +13567,7 @@ namespace cimg_library_suffixed { i1 = cimg::float2uint((float)*(ptrs++)); if (nb_inds==6) ptrs+=4; if (i0>=nb_points || i1>=nb_points) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " "segment primitive [%u]", nb_points,nb_primitives,i0,i1,p); @@ -13686,7 +13581,7 @@ namespace cimg_library_suffixed { i2 = cimg::float2uint((float)*(ptrs++)); if (nb_inds==9) ptrs+=6; if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " "triangle primitive [%u]", nb_points,nb_primitives,i0,i1,i2,p); @@ -13701,7 +13596,7 @@ namespace cimg_library_suffixed { i3 = cimg::float2uint((float)*(ptrs++)); if (nb_inds==12) ptrs+=8; if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " "quadrangle primitive [%u]", nb_points,nb_primitives,i0,i1,i2,i3,p); @@ -13709,13 +13604,13 @@ namespace cimg_library_suffixed { } } break; default : - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", nb_points,nb_primitives,p,nb_inds); return false; } if (ptrs>ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " "%u values missing", nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); @@ -13725,7 +13620,7 @@ namespace cimg_library_suffixed { // Check consistency of color data. if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines no color/texture data", nb_points,nb_primitives); return false; @@ -13739,7 +13634,7 @@ namespace cimg_library_suffixed { s = (unsigned int)*(ptrs - 1); if (!h && !s) { if (w>=c) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u " "for primitive [%u]", nb_points,nb_primitives,w,c); @@ -13748,7 +13643,7 @@ namespace cimg_library_suffixed { } else ptrs+=w*h*s; } if (ptrs>ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " "%u values missing", nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); @@ -13758,7 +13653,7 @@ namespace cimg_library_suffixed { // Check consistency of opacity data. if (ptrs==ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) defines no opacity data", nb_points,nb_primitives); return false; @@ -13771,7 +13666,7 @@ namespace cimg_library_suffixed { s = (unsigned int)*(ptrs - 1); if (!h && !s) { if (w>=o) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) refers to invalid shared opacity indice %u " "for primitive [%u]", nb_points,nb_primitives,w,o); @@ -13780,7 +13675,7 @@ namespace cimg_library_suffixed { } else ptrs+=w*h*s; } if (ptrs>ptre) { - if (error_message) std::sprintf(error_message, + if (error_message) cimg_sprintf(error_message, "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", nb_points,nb_primitives,o); return false; @@ -13789,7 +13684,7 @@ namespace cimg_library_suffixed { // Check end of data. if (ptrs1?"s":""); return false; @@ -13808,1106 +13703,5206 @@ namespace cimg_library_suffixed { //@{ //------------------------------------- - // Define the math formula parser/compiler and evaluator. + // Define the math formula parser/compiler and expression evaluator. struct _cimg_math_parser { - CImgList code; - CImg opcode; - const CImg* p_code; - CImgList labelM; - CImg level, labelMpos, reserved_label; CImg mem; - CImg expr; - const CImg& reference; - CImg reference_stats; - double median_value; - bool is_median_value; - unsigned int mempos, result; + CImg memtype; + CImgList _code, &code; + CImg opcode; + const CImg *p_code_begin, *p_code_end, *p_code; + + CImg expr, pexpr; + const CImg& imgin; + const CImgList& listin; + CImg &imgout; + CImgList& listout; + + CImg _img_stats, &img_stats; + CImgList _list_stats, &list_stats, _list_median, &list_median; + CImg mem_img_stats; + + CImg level, variable_pos, reserved_label; + CImgList variable_def, function_def, function_body; + char *user_function; + + unsigned int mempos, mem_img_median, debug_indent, init_size, result_dim; + double *result; const char *const calling_function; typedef double (*mp_func)(_cimg_math_parser&); +#define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant? +#define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar? +#define _cimg_mp_is_temp(arg) (!memtype[arg]) // Is temporary scalar? +#define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable? +#define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? +#define _cimg_mp_vector_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Vector size +#define _cimg_mp_calling_function calling_function_s()._data +#define _cimg_mp_check_type(arg,n_arg,s_op,mode,N) check_type(arg,n_arg,s_op,mode,N,ss,se,saved_char) +#define _cimg_mp_check_constant(arg,n_arg,s_op,is_strict) check_constant(arg,n_arg,s_op,is_strict,ss,se,saved_char) +#define _cimg_mp_check_matrix_square(arg,n_arg,s_op) check_matrix_square(arg,n_arg,s_op,ss,se,saved_char) +#define _cimg_mp_check_vector0(dim,s_op) check_vector0(dim,s_op,ss,se,saved_char) +#define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) #define _cimg_mp_return(x) { *se = saved_char; return x; } -#define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op)); -#define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1)); -#define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); } -#define _cimg_mp_opcode3(op,i1,i2,i3) \ - { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3; _cimg_mp_return(opcode3(op,_i1,_i2,_i3)); } -#define _cimg_mp_opcode6(op,i1,i2,i3,i4,i5,i6) \ - { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5, _i6 = i6; \ - _cimg_mp_return(opcode6(op,_i1,_i2,_i3,_i4,_i5,_i6)); } - -#if defined(_WIN64) - // On Win64 and gcc 4.7, sizeof(long)!=sizeof(pointer), so a workaround is needed.. -#define _cimg_mp_enfunc(op) (long)((char*)(op) - (char*)mp_u) -#define _cimg_mp_defunc(mp) (*(mp_func)((char*)mp_u + (mp).opcode[0]))(mp) -#else -#define _cimg_mp_enfunc(op) (long)(op) -#define _cimg_mp_defunc(mp) (*(mp_func)((mp).opcode[0]))(mp) -#endif +#define _cimg_mp_constant(val) _cimg_mp_return(constant(val)) +#define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) +#define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) +#define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2)) +#define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3)) +#define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6)) +#define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7)) +#define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1)) +#define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2)) +#define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2)) +#define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2)) +#define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3)) // Constructors. - _cimg_math_parser():reference(CImg::empty()),median_value(0),is_median_value(false),calling_function(0) {} - - _cimg_math_parser(const CImg& img, const char *const expression, const char *const funcname=0): - reference(img),median_value(0),is_median_value(false),calling_function(funcname?funcname:"cimg_math_parser") { + _cimg_math_parser(const char *const expression, const char *const funcname=0, + const CImg& img_input=CImg::const_empty(), CImg *const img_output=0, + const CImgList *const list_input=0, CImgList *const list_output=0): + code(_code),imgin(img_input),listin(list_input?*list_input:CImgList::const_empty()), + imgout(img_output?*img_output:CImg::empty()),listout(list_output?*list_output:CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),user_function(0), + mem_img_median(~0U),debug_indent(0),init_size(0),result_dim(0), + calling_function(funcname?funcname:"cimg_math_parser") { if (!expression || !*expression) throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Empty specified expression.", - pixel_type(),calling_function); - CImg::string(expression).move_to(expr); + "CImg<%s>::%s: Empty expression.", + pixel_type(),_cimg_mp_calling_function); + const char *_expression = expression; + while (*_expression && *_expression<=' ') ++_expression; + CImg::string(_expression).move_to(expr); + + // Ease the retrieval of previous non-space characters afterwards. + pexpr.assign(expr._width); + const char *ps; + char c, *pe = pexpr._data; + for (ps = expr._data, c = ' '; *ps; ++ps) { + if (*ps!=' ') c = *ps; + *(pe++) = c; + } + *pe = 0; + + // Count parentheses/brackets level of expression. level.assign(expr._width - 1); - int lv = 0; // Count parentheses/brackets level of expression. + int lv = 0; unsigned int *pd = level._data; - for (const char *ps = expr._data; *ps && lv>=0; ++ps) + for (ps = expr._data; *ps && lv>=0; ++ps) *(pd++) = (unsigned int)(*ps=='('||*ps=='['?lv++:*ps==')'||*ps==']'?--lv:lv); if (lv!=0) { + cimg::strellipsize(expr,64); throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Unbalanced parentheses/brackets in specified expression '%s'.", - pixel_type(),calling_function, + "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, expr._data); } // Init constant values. - mem.assign(512); - mem[0] = 0.0; - mem[1] = 1.0; - mem[2] = 2.0; - mem[3] = 3.0; - mem[4] = 4.0; - mem[5] = 5.0; - mem[6] = (double)reference._width; - mem[7] = (double)reference._height; - mem[8] = (double)reference._depth; - mem[9] = (double)reference._spectrum; - mem[10] = (double)reference._is_shared; - mem[11] = (double)reference._width*reference._height; - mem[12] = (double)reference._width*reference._height*reference._depth; - mem[13] = (double)reference._width*reference._height*reference._depth*reference._spectrum; - mem[14] = cimg::PI; - mem[15] = std::exp(1.0); // Then [16] = x, [17] = y, [18] = z and [19] = c. - mempos = 20; - labelMpos.assign(8); + mem.assign(96); + memtype.assign(96); + double *p_mem = mem._data; + for (unsigned int i = 0; i<=10; ++i) *(p_mem++) = (double)i; // mem[0-10] + for (unsigned int i = 1; i<=5; ++i) *(p_mem++) = -(double)i; // mem[11-15] + *(p_mem++) = 0.5; // mem[16] + *(p_mem++) = 0; // mem[17] = thread_id + *(p_mem++) = (double)imgin._width; // mem[18] + *(p_mem++) = (double)imgin._height; // mem[19] + *(p_mem++) = (double)imgin._depth; // mem[20] + *(p_mem++) = (double)imgin._spectrum; // mem[21] + *(p_mem++) = (double)imgin._is_shared; // mem[22] + *(p_mem++) = (double)imgin._width*imgin._height; // mem[23] + *(p_mem++) = (double)imgin._width*imgin._height*imgin._depth; // mem[24] + *(p_mem++) = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // mem[25] + *(p_mem++) = cimg::PI; // mem[26] + *(p_mem++) = std::exp(1.0); // mem[27] + *(p_mem++) = cimg::type::nan(); // mem[28] + + // Then, [29] = x, [30] = y, [31] = z and [32] = c. +#define _cimg_mp_x 29 +#define _cimg_mp_y 30 +#define _cimg_mp_z 31 +#define _cimg_mp_c 32 + + // Set value property : + // { -1 = variable | 0 = regular value | 1 = compile time constant | N>1 = constant ptr to vector[N-1] }. + std::memset(memtype._data,0,sizeof(int)*memtype._width); + int *p_memtype = memtype._data; for (unsigned int i = 0; i<_cimg_mp_x; ++i) *(p_memtype++) = 1; + memtype[17] = 0; + + mempos = _cimg_mp_c + 1; + variable_pos.assign(8); reserved_label.assign(128,1,1,1,~0U); - reserved_label['w'] = 6; - reserved_label['h'] = 7; - reserved_label['d'] = 8; - reserved_label['s'] = 9; - reserved_label['r'] = 10; - reserved_label[0] = 11; // wh - reserved_label[1] = 12; // whd - reserved_label[2] = 13; // whds - reserved_label[3] = 14; // pi - reserved_label['e'] = 15; - reserved_label['x'] = 16; - reserved_label['y'] = 17; - reserved_label['z'] = 18; - reserved_label['c'] = 19; - result = compile(expr._data,expr._data + expr._width - 1); // Compile formula into a serie of opcodes. - } - - // Insert code instructions. - unsigned int opcode0(const mp_func op) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos).move_to(code); - return pos; - } - - unsigned int opcode1(const mp_func op, const unsigned int arg1) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1).move_to(code); - return pos; - } - - unsigned int opcode2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1,arg2).move_to(code); - return pos; + reserved_label['t'] = 17; + reserved_label['w'] = 18; + reserved_label['h'] = 19; + reserved_label['d'] = 20; + reserved_label['s'] = 21; + reserved_label['r'] = 22; + reserved_label[0] = 23; // wh + reserved_label[1] = 24; // whd + reserved_label[2] = 25; // whds + reserved_label[3] = 26; // pi + reserved_label['e'] = 27; + reserved_label[29] = 0; // interpolation + reserved_label[30] = 0; // boundary + reserved_label['x'] = _cimg_mp_x; + reserved_label['y'] = _cimg_mp_y; + reserved_label['z'] = _cimg_mp_z; + reserved_label['c'] = _cimg_mp_c; + // reserved_label[4-28] store also two-char variables: + // [4] = im, [5] = iM, [6] = ia, [7] = iv, [8] = is, [9] = ip, [10] = ic, + // [11] = xm, [12] = ym, [13] = zm, [14] = cm, [15] = xM, [16] = yM, [17] = zM, [18]=cM, [19]=i0...[28]=i9, + + // Compile expression into a serie of opcodes. + const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0); + p_code_end = code.end(); + + // Free resources used for parsing and prepare for evaluation. + if (_cimg_mp_is_vector(ind_result)) result_dim = _cimg_mp_vector_size(ind_result); + mem.resize(mempos,1,1,1,-1); + result = mem._data + ind_result; + memtype.assign(); + level.assign(); + variable_pos.assign(); + reserved_label.assign(); + expr.assign(); + pexpr.assign(); + opcode._width = opcode._depth = opcode._spectrum = 1; + opcode._is_shared = true; + + // Execute init() function if any specified. + p_code_begin = code._data + init_size; + if (init_size) { + mem[_cimg_mp_x] = mem[_cimg_mp_y] = mem[_cimg_mp_z] = mem[_cimg_mp_c] = 0; + for (p_code = code._data; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + const uptrT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + } + + _cimg_math_parser(): + code(_code),p_code_begin(0),p_code_end(0), + imgin(CImg::const_empty()),listin(CImgList::const_empty()), + imgout(CImg::empty()),listout(CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),debug_indent(0), + result_dim(0),calling_function(0) { + mem.assign(1 + _cimg_mp_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() + result = mem._data; + } + + _cimg_math_parser(const _cimg_math_parser& mp): + mem(mp.mem),code(mp.code),p_code_begin(mp.p_code_begin),p_code_end(mp.p_code_end), + imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),img_stats(mp.img_stats), + list_stats(mp.list_stats),list_median(mp.list_median),debug_indent(0), + result_dim(mp.result_dim), result(mem._data + (mp.result - mp.mem._data)),calling_function(0) { +#ifdef cimg_use_openmp + mem[17] = omp_get_thread_num(); +#endif + opcode._width = opcode._depth = opcode._spectrum = 1; + opcode._is_shared = true; + } + + // Return 'true' is the specified mathematical expression requires the input image to be copied. + // Set 'is_parallelizable' to 'false' if expression should be evaluated with a single thread. + static bool needs_input_copy(const char *expression, bool &is_parallelizable) { + if (!expression || *expression=='>' || *expression=='<') return is_parallelizable = false; + for (const char *s = expression; *s; ++s) + if ((*s=='i' || *s=='j' || *s=='I' || *s=='J') && (s[1]=='(' || s[1]=='[')) { + if (s[2]=='#') is_parallelizable = false; + else { + const char opening = s[1], ending = opening=='('?')':']'; + const char *ns; + int level = 0; + for (ns = s + 2; *ns; ++ns) { // Find ending ')' or ']'. + if (*ns==ending && !level) break; + if (*ns==opening) ++level; else if (*ns==ending) --level; + } + if (*ns && (ns[1]!='=' || ns[2]=='=')) return true; + } + } else if (((*s=='R' || *s=='G' || *s=='B' || *s=='A' || *s=='I' || *s=='J') && s[1]!='#') || + (*s=='i' && s[1]>='0' && s[1]<='7' && s[2]!='#')) return true; + return false; } - unsigned int opcode3(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1,arg2,arg3).move_to(code); - return pos; - } + // Compilation procedure. + unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *p_ref) { + if (depth>256) { + cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: Call stack overflow (infinite recursion?), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } - unsigned int opcode6(const mp_func op, - const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, - const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) { - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(op),pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); - return pos; - } + const char *const ss0 = ss; + char c1, c2, c3, c4; - // Compilation procedure. - unsigned int compile(char *ss, char *se) { - while (*ss==' ') ++ss; - while (se>ss && *(se-1)==' ') --se; + if (ssss && (c1=*(se - 1))>0 && (c1<=' ' || c1==';')) --se; + } + if (se>ss && *(se - 1)==';') --se; + while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Detect simple content around parentheses. + ++ss; --se; + } if (se<=ss || !*ss) { + cimg::strellipsize(expr,64); throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Missing item in specified expression '%s'.", - pixel_type(),calling_function, + "CImg<%s>::%s: Missing item, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, expr._data); } + const unsigned int depth1 = depth + 1; + unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6; char - *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, *const se4 = se - 4, + *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4, - *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7; + *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8, + *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0; + double val, val1, val2; + const char *s_op; + mp_func op; + + // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value + // linked to the returned memory slot (reference that cannot be determined at compile time). + // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) | + // 3 = image value (coordinates) | 4 = image value as a vector (offsets) | + // 5 = image value as a vector (coordinates) }. + // Depending on p_ref[0], the remaining p_ref[k] have the following meaning: + // When p_ref[0]==0, p_ref is actually unlinked. + // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ]. + // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ]. + // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ]. + if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; } + const char saved_char = *se; *se = 0; const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; - if (*se1==';') return compile(ss,se1); + bool is_sth, is_relative; + CImg ref; + CImgList _opcode; + CImg variable_name; - // Look for a single value, variable or variable assignment. - char end = 0, sep = 0; double val = 0; - int nb = cimg_sscanf(ss,"%lf%c%c",&val,&sep,&end); + // Look for a single value or a pre-defined variable. + int nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); #if cimg_OS==2 // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able // to read those particular values. if (!nb && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) { - bool is_positive = true; - const char *_ss = ss; - if (*_ss=='+') ++_ss; else if (*_ss=='-') { ++_ss; is_positive = false; } - if (!cimg::strcasecmp(_ss,"inf")) { val = cimg::type::inf(); nb = 1; } - else if (!cimg::strcasecmp(_ss,"nan")) { val = cimg::type::nan(); nb = 1; } - if (nb==1 && !is_positive) val = -val; - } -#endif - - if (nb==1) { - if (val==0 || val==1 || val==2 || val==3 || val==4 || val==5) - _cimg_mp_return((unsigned int)val); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = val; - _cimg_mp_return(pos); - } - if (nb==2 && sep=='%') { - if (val==0 || val==100 || val==200 || val==300 || val==400 || val==500) - _cimg_mp_return((unsigned int)val/100); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = val/100; - _cimg_mp_return(pos); - } - if (ss1==se) switch (*ss) { // One-char variable. - case 'w' : case 'h' : case 'd' : case 's' : case 'r' : - case 'x' : case 'y' : case 'z' : case 'c' : case 'e' : _cimg_mp_return(reserved_label[*ss]); - case 'u' : if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']); _cimg_mp_opcode2(mp_u,0,1); - case 'g' : if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']); _cimg_mp_opcode0(mp_g); - case 'i' : if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']); _cimg_mp_opcode0(mp_i); - case '?' : _cimg_mp_opcode2(mp_u,0,1); - } - else if (ss2==se) { // Two-chars variable. + is_sth = true; + s = ss; + if (*s=='+') ++s; else if (*s=='-') { ++s; is_sth = false; } + if (!cimg::strcasecmp(s,"inf")) { val = cimg::type::inf(); nb = 1; } + else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type::nan(); nb = 1; } + if (nb==1 && !is_sth) val = -val; + } +#endif + if (nb==1) _cimg_mp_constant(val); + if (nb==2 && sep=='%') _cimg_mp_constant(val/100); + + if (ss1==se) switch (*ss) { // One-char variable + case 't' : case 'w' : case 'h' : case 'd' : case 's' : case 'r' : + case 'x' : case 'y' : case 'z' : case 'c' : case 'e' : + _cimg_mp_return(reserved_label[*ss]); + case 'u' : + if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']); + _cimg_mp_scalar2(mp_u,0,1); + case 'g' : + if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']); + _cimg_mp_scalar0(mp_g); + case 'i' : + if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']); + _cimg_mp_scalar0(mp_i); + case 'I' : + if (reserved_label['I']!=~0U) _cimg_mp_return(reserved_label['I']); + _cimg_mp_check_vector0(imgin._spectrum,"variable 'I'"); + pos = vector(imgin._spectrum); + CImg::vector((uptrT)mp_Joff,pos,0,0).move_to(code); + _cimg_mp_return(pos); + case 'R' : + if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']); + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,0,0,0); + case 'G' : + if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']); + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,1,0,0); + case 'B' : + if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']); + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,2,0,0); + case 'A' : + if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']); + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,3,0,0); + } + else if (ss2==se) { // Two-chars variable + arg1 = arg2 = ~0U; if (*ss=='w' && *ss1=='h') _cimg_mp_return(reserved_label[0]); // wh if (*ss=='p' && *ss1=='i') _cimg_mp_return(reserved_label[3]); // pi - if (*ss=='i') { // im - if (*ss1=='m') { - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[4]!=~0U) _cimg_mp_return(reserved_label[4]); _cimg_mp_opcode0(mp_im); + if (*ss=='i') { + if (*ss1>='0' && *ss1<='9') { // i0...i9 + pos = 19 + *ss1 - '0'; + if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,pos - 19,0,0); + } + switch (*ss1) { + case 'm' : arg1 = 4; arg2 = 0; break; // im + case 'M' : arg1 = 5; arg2 = 1; break; // iM + case 'a' : arg1 = 6; arg2 = 2; break; // ia + case 'v' : arg1 = 7; arg2 = 3; break; // iv + case 's' : arg1 = 8; arg2 = 12; break; // is + case 'p' : arg1 = 9; arg2 = 13; break; // is + case 'c' : // ic + if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); + if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; + _cimg_mp_return(mem_img_median); + break; } - if (*ss1=='M') { // iM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[5]!=~0U) _cimg_mp_return(reserved_label[5]); _cimg_mp_opcode0(mp_iM); + } + else if (*ss1=='m') switch (*ss) { + case 'x' : arg1 = 11; arg2 = 4; break; // xm + case 'y' : arg1 = 12; arg2 = 5; break; // ym + case 'z' : arg1 = 13; arg2 = 6; break; // zm + case 'c' : arg1 = 14; arg2 = 7; break; // cm + } + else if (*ss1=='M') switch (*ss) { + case 'x' : arg1 = 15; arg2 = 8; break; // xM + case 'y' : arg1 = 16; arg2 = 9; break; // yM + case 'z' : arg1 = 17; arg2 = 10; break; // zM + case 'c' : arg1 = 18; arg2 = 11; break; // cM } - if (*ss1=='a') { // ia - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[6]!=~0U) _cimg_mp_return(reserved_label[6]); _cimg_mp_opcode0(mp_ia); + if (arg1!=~0U) { + if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); + if (!img_stats) { + img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false); + mem_img_stats.assign(1,14,1,1,~0U); + } + if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]); + _cimg_mp_return(mem_img_stats[arg2]); + } + } else if (ss3==se) { // Three-chars variable + if (*ss=='w' && *ss1=='h' && *ss2=='d') _cimg_mp_return(reserved_label[1]); // whd + } else if (ss4==se) { // Four-chars variable + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') _cimg_mp_return(reserved_label[2]); // whds + } + + pos = ~0U; + for (s0 = ss, s = ss1; s2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') && + (reserved_label[*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[*ss]))) { + is_relative = *ss=='j' || *ss=='J'; + + if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0='i'?1:3,p2); + + if (p_ref) { + *p_ref = _cimg_mp_is_vector(arg2)?4:2; + p_ref[1] = p1; + p_ref[2] = (unsigned int)is_relative; + p_ref[3] = arg1; + if (_cimg_mp_is_vector(arg2)) + set_variable_vector(arg2); // Prevent from being used in further optimization + else if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; + if (p1!=~0U && _cimg_mp_is_temp(p1)) memtype[p1] = -1; + if (_cimg_mp_is_temp(arg1)) memtype[arg1] = -1; + } + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg1).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg1).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg1).move_to(code); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg1).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg1).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + } else if (s1='i'?1:3,p2); + + if (p_ref) { + *p_ref = _cimg_mp_is_vector(arg5)?5:3; + p_ref[1] = p1; + p_ref[2] = (unsigned int)is_relative; + p_ref[3] = arg1; + p_ref[4] = arg2; + p_ref[5] = arg3; + p_ref[6] = arg4; + if (_cimg_mp_is_vector(arg5)) + set_variable_vector(arg5); // Prevent from being used in further optimization + else if (_cimg_mp_is_temp(arg5)) memtype[arg5] = -1; + if (p1!=~0U && _cimg_mp_is_temp(p1)) memtype[p1] = -1; + if (_cimg_mp_is_temp(arg1)) memtype[arg1] = -1; + if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; + if (_cimg_mp_is_temp(arg3)) memtype[arg3] = -1; + if (_cimg_mp_is_temp(arg4)) memtype[arg4] = -1; + } + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg5); + if (*ss>='i') + CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg5,p1,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg5,p1,arg1,arg2,arg3).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg5,p1,arg1,arg2,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg5); + if (*ss>='i') + CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg5,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((uptrT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg5,arg1,arg2,arg3).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg5,arg1,arg2,arg3).move_to(code); + } + _cimg_mp_return(arg5); + } } - if (*ss1=='v') { // iv - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[7]!=~0U) _cimg_mp_return(reserved_label[7]); _cimg_mp_opcode0(mp_iv); + + // Assign vector value (direct). + if (l_variable_name>3 && *ve1==']' && *ss!='[') { + s0 = ve1; while (s0>ss && *s0!='[') --s0; + is_sth = true; // is_valid_variable_name? + if (*ss>='0' && *ss<='9') is_sth = false; + else for (ns = ss; nsss) { + variable_name[s0 - ss] = 0; // Remove brackets in variable name + arg1 = ~0U; // Vector slot + arg2 = compile(++s0,ve1,depth1,0); // Index + arg3 = compile(s + 1,se,depth1,0); // Value to assign + _cimg_mp_check_type(arg3,2,s_op,1,0); + + if (variable_name[1]) { // Multi-char variable + cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i])) { + arg1 = variable_pos[i]; break; + } + } else arg1 = reserved_label[*variable_name]; // Single-char variable + if (arg1==~0U) compile(ss,s0 - 1,depth1,0); // Variable does not exist -> error + else { // Variable already exists + if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0); // Variable is not a vector -> error + if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_vector_size(arg1)) { + arg1+=nb + 1; + CImg::vector((uptrT)mp_copy,arg1,arg3).move_to(code); + _cimg_mp_return(arg1); + } + compile(ss,s,depth1,0); // Out-of-bounds reference -> error + } + + // Case of non-constant index -> return assigned value + linked reference + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_temp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization + if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; + } + CImg::vector((uptrT)mp_vector_set_off,arg3,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2,arg3). + move_to(code); + _cimg_mp_return(arg3); + } + } } - if (*ss1=='s') { // is - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[8]!=~0U) _cimg_mp_return(reserved_label[8]); _cimg_mp_opcode0(mp_is); + + // Assign user-defined function. + if (l_variable_name>3 && *ve1==')' && *ss!='(') { + s0 = ve1; while (s0>ss && *s0!='(') --s0; + is_sth = std::strncmp(variable_name,"debug(",6) && + std::strncmp(variable_name,"print(",6); // is_valid_function_name? + if (*ss>='0' && *ss<='9') is_sth = false; + else for (ns = ss; nsss) { // Looks like a valid function declaration + s0 = variable_name._data + (s0 - ss); + *s0 = 0; + s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis + CImg(variable_name._data,s0 - variable_name._data + 1).move_to(function_def,0); + ++s; while (*s && *s<=' ') ++s; + CImg(s,se - s + 1).move_to(function_body,0); + + p1 = 1; // Indice of current parsed argument + for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments + if (p1>24) { + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Too much specified arguments (>24) when defining " + "function '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + while (*s && *s<=' ') ++s; + if (*s==')' && p1==1) break; // Function has no arguments + + s2 = s; // Start of the argument name + is_sth = true; // is_valid_argument_name? + if (*s>='0' && *s<='9') is_sth = false; + else for (ns = s; ns' '; ++ns) + if (!is_varchar(*ns)) { is_sth = false; break; } + s3 = ns; // End of the argument name + while (*ns && *ns<=' ') ++ns; + if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) { + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: %s name specified for argument %u when defining " + "function '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + is_sth?"Empty":"Invalid",p1, + variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + if (ns==s1 || *ns==',') { // New argument found + *s3 = 0; + p2 = s3 - s2; // Argument length + p3 = function_body[0]._width - p2 + 1; // Related to copy length + for (ps = std::strstr(function_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number + if (!((ps>function_body[0]._data && is_varchar(*(ps - 1))) || + (ps + p21) { + std::memmove(ps,ps + p2 - 1,function_body[0]._data + p3 - ps); + function_body[0]._width-=p2 - 1; + } + } else ++ps; + } + } + } + // Store number of arguments + function_def[0].resize(function_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1); + _cimg_mp_return(28); + } } - if (*ss1=='p') { // ip - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[9]!=~0U) _cimg_mp_return(reserved_label[9]); _cimg_mp_opcode0(mp_ip); + + // Check if the variable name could be valid. If not, this is probably an lvalue assignment. + is_sth = true; // is_valid_variable_name? + if (*variable_name>='0' && *variable_name<='9') is_sth = false; + else for (ns = variable_name._data; *ns; ++ns) + if (!is_varchar(*ns)) { is_sth = false; break; } + + // Assign variable (direct). + if (is_sth) { + if (variable_name[1] && !variable_name[2]) { // Two-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + if (c1=='w' && c2=='h') variable_name.fill((char)0,(char)0); // wh + else if (c1=='p' && c2=='i') variable_name.fill(3,0); // pi + else if (c1=='i') { + if (c2>='0' && c2<='9') variable_name.fill(19 + c2 - '0',0); // i0...i9 + else if (c2=='m') variable_name.fill(4,0); // im + else if (c2=='M') variable_name.fill(5,0); // iM + else if (c2=='a') variable_name.fill(6,0); // ia + else if (c2=='v') variable_name.fill(7,0); // iv + else if (c2=='s') variable_name.fill(8,0); // is + else if (c2=='p') variable_name.fill(9,0); // ip + else if (c2=='c') variable_name.fill(10,0); // ic + } else if (c2=='m') { + if (c1=='x') variable_name.fill(11,0); // xm + else if (c1=='y') variable_name.fill(12,0); // ym + else if (c1=='z') variable_name.fill(13,0); // zm + else if (c1=='c') variable_name.fill(14,0); // cm + } else if (c2=='M') { + if (c1=='x') variable_name.fill(15,0); // xM + else if (c1=='y') variable_name.fill(16,0); // yM + else if (c1=='z') variable_name.fill(17,0); // zM + else if (c1=='c') variable_name.fill(18,0); // cM + } + } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + if (c1=='w' && c2=='h' && c3=='d') variable_name.fill(1,0); // whd + } else if (variable_name[1] && variable_name[2] && variable_name[3] && + !variable_name[4]) { // Four-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + c4 = variable_name[3]; + if (c1=='w' && c2=='h' && c3=='d' && c4=='s') variable_name.fill(2,0); // whds + } else if (!std::strcmp(variable_name,"interpolation")) variable_name.fill(29,0); + else if (!std::strcmp(variable_name,"boundary")) variable_name.fill(30,0); + + arg1 = ~0U; + arg2 = compile(s + 1,se,depth1,0); + if (!variable_name[1]) // One-char variable, or variable in reserved_labels + arg1 = reserved_label[*variable_name]; + else // Multi-char variable name : check for existing variable with same name + cimglist_for(variable_def,i) + if (!std::strcmp(variable_name,variable_def[i])) { arg1 = variable_pos[i]; break; } + + if (arg1==~0U || arg1<=_cimg_mp_c) { // Create new variable + if (_cimg_mp_is_vector(arg2)) { // Vector variable + arg1 = vector_copy(arg2); + set_variable_vector(arg1); + } else { // Scalar variable + arg1 = scalar1(mp_copy,arg2); + memtype[arg1] = -1; + } + + if (!variable_name[1]) reserved_label[*variable_name] = arg1; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg1; + variable_name.move_to(variable_def); + } + + } else { // Variable already exists -> assign a new value + _cimg_mp_check_type(arg2,2,s_op,_cimg_mp_is_vector(arg1)?3:1,0); + if (_cimg_mp_is_vector(arg1)) { // Vector + if (_cimg_mp_is_vector(arg2)) // From vector + CImg::vector((uptrT)mp_vector_copy,arg1,arg2,(uptrT)_cimg_mp_vector_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((uptrT)mp_vector_init,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2). + move_to(code); + } else // Scalar + CImg::vector((uptrT)mp_copy,arg1,arg2).move_to(code); + } + _cimg_mp_return(arg1); + } + + // Assign lvalue (variable name was not valid). + is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? + if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment + + if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { + ref.assign(7); + arg1 = compile(ss,s,depth1,ref); // Lvalue slot + arg2 = compile(s + 1,se,depth1,0); // Value to assign + + if (*ref==1) { // Vector value (scalar): V[k] = scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)mp_vector_set_off,arg2,arg3,(uptrT)_cimg_mp_vector_size(arg3),arg4,arg2). + move_to(code); + _cimg_mp_return(arg2); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg3).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg2,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg2,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value + _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg3).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg3).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg3).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value + _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg2,p1,arg3,arg4,arg5).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg2,p1,arg3,arg4,arg5).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg2,arg3,arg4,arg5).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg2,arg3,arg4,arg5).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value + _cimg_mp_check_type(arg2,2,s_op,1,0); + if (_cimg_mp_is_vector(arg2)) // From vector + CImg::vector((uptrT)mp_vector_copy,arg1,arg2,(uptrT)_cimg_mp_vector_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((uptrT)mp_vector_init,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2). + move_to(code); + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + CImg::vector((uptrT)mp_copy,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + + } } - if (*ss1=='c') { // ic - if (!is_median_value && reference) { median_value = reference.median(); is_median_value = true; } - if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); _cimg_mp_opcode0(mp_ic); + + // No assignment expressions match -> error + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Invalid left-hand operand '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + + // Apply unary/binary/ternary operators. The operator precedences should be roughly the same as in C++. + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps && + level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=) + s_op = *ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"; + + ref.assign(7); + arg1 = compile(ss,ns,depth1,ref); // Vector slot + arg2 = compile(s + 1,se,depth1,0); // Right operand + if (*ps!='*') { + _cimg_mp_check_type(arg1,2,s_op,2,2); + _cimg_mp_check_type(arg2,2,s_op,2,2); + } + if (_cimg_mp_is_vector(arg2)) { // Complex **= complex or Matrix **= matrix + if (*ps=='*') { + if (_cimg_mp_vector_size(arg1)==2 && _cimg_mp_vector_size(arg2)==2) + CImg::vector((uptrT)mp_complex_mul,arg1,arg1,arg2).move_to(code); + else { + _cimg_mp_check_matrix_square(arg2,2,s_op); + p3 = _cimg_mp_vector_size(arg1); + p2 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg2)); + p1 = p3/p2; + if (p1*p2!=p3) { + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Sizes of left-hand and right-hand operands " + "('%s' and '%s') do not match, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + CImg::vector((uptrT)mp_matrix_mul,arg1,arg1,arg2,p1,p2,p2).move_to(code); + } + } else if (*ps=='/') + CImg::vector((uptrT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code); + else + CImg::vector((uptrT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code); + } else { // Complex **= scalar + if (*ps=='*') + CImg::vector((uptrT)mp_self_map_vector_s,arg1,2,(uptrT)mp_self_mul,arg2).move_to(code); + else if (*ps=='/') + CImg::vector((uptrT)mp_self_map_vector_s,arg1,2,(uptrT)mp_self_div,arg2).move_to(code); + else + CImg::vector((uptrT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code); + } + + // Write computed value back in image if necessary. + if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3).move_to(code); + } + + } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5).move_to(code); + } } + + _cimg_mp_return(arg1); + } + + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' || + *ps=='&' || *ps=='^' || *ps=='|' || + (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && + level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=) + switch (*ps) { + case '+' : op = mp_self_add; s_op = "Operator '+='"; break; + case '-' : op = mp_self_sub; s_op = "Operator '-='"; break; + case '*' : op = mp_self_mul; s_op = "Operator '*='"; break; + case '/' : op = mp_self_div; s_op = "Operator '/='"; break; + case '%' : op = mp_self_modulo; s_op = "Operator '%='"; break; + case '<' : op = mp_self_bitwise_left_shift; s_op = "Operator '<<='"; break; + case '>' : op = mp_self_bitwise_right_shift; s_op = "Operator '>=='"; break; + case '&' : op = mp_self_bitwise_and; s_op = "Operator '&='"; break; + case '|' : op = mp_self_bitwise_or; s_op = "Operator '|='"; break; + default : op = mp_self_pow; s_op = "Operator '^='"; break; + } + s1 = *ps=='>' || *ps=='<'?ns:ps; + + ref.assign(7); + arg1 = compile(ss,s1,depth1,ref); // Variable slot + arg2 = compile(s + 1,se,depth1,0); // Value to apply + + if (*ref>0 && !_cimg_mp_is_temp(arg1)) { // Apply operator on a copy if necessary. + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k] += scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)op,arg1,arg2).move_to(code); + CImg::vector((uptrT)mp_vector_set_off,arg1,arg3,(uptrT)_cimg_mp_vector_size(arg3),arg4,arg1). + move_to(code); + _cimg_mp_return(arg1); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value + _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)mp_self_map_vector_s,arg1,(uptrT)_cimg_mp_vector_size(arg1),(uptrT)op,arg2). + move_to(code); + else + CImg::vector((uptrT)mp_self_map_vector_v,arg1,(uptrT)_cimg_mp_vector_size(arg1),(uptrT)op,arg2). + move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value + _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((uptrT)mp_self_map_vector_s,arg1,(uptrT)_cimg_mp_vector_size(arg1),(uptrT)op,arg2). + move_to(code); + else + CImg::vector((uptrT)mp_self_map_vector_v,arg1,(uptrT)_cimg_mp_vector_size(arg1),(uptrT)op,arg2). + move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value + _cimg_mp_check_type(arg2,2,s_op,3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg2)) // Vector += vector + CImg::vector((uptrT)mp_self_map_vector_v,arg1,(uptrT)_cimg_mp_vector_size(arg1),(uptrT)op,arg2). + move_to(code); + else // Vector += scalar + CImg::vector((uptrT)mp_self_map_vector_s,arg1,(uptrT)_cimg_mp_vector_size(arg1),(uptrT)op,arg2). + move_to(code); + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar + _cimg_mp_check_type(arg2,2,s_op,1,0); + CImg::vector((uptrT)op,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + } + + variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0; + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Invalid left-hand operand '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + + for (s = ss1; s::vector((uptrT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') + s_op = "Operator '||'"; + arg1 = compile(ss,s,depth1,0); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg1,1,s_op,1,0); + _cimg_mp_check_type(arg2,2,s_op,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] || mem[arg2]); + pos = scalar(); + CImg::vector((uptrT)mp_logical_or,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') + s_op = "Operator '&&'"; + arg1 = compile(ss,s,depth1,0); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg1,1,s_op,1,0); + _cimg_mp_check_type(arg2,2,s_op,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] && mem[arg2]); + pos = scalar(); + CImg::vector((uptrT)mp_logical_and,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + _cimg_mp_return(pos); } - if (*ss1=='m') { - if (*ss=='x') { // xm - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); _cimg_mp_opcode0(mp_xm); + + for (s = se2; s>ss; --s) + if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '|'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((unsigned long)mem[arg1] | (unsigned long)mem[arg2]); + _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2); + } + + for (s = se2; s>ss; --s) + if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '&'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((unsigned long)mem[arg1] & (unsigned long)mem[arg2]); + _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '!='",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_neq,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_neq,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_neq,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]); + _cimg_mp_scalar2(mp_neq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '=='",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_eq,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_eq,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_eq,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]); + _cimg_mp_scalar2(mp_eq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '<='",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]); + _cimg_mp_scalar2(mp_lte,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '>='",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]); + _cimg_mp_scalar2(mp_gte,arg1,arg2); + } + + for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) + if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '<'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]ss; --s, --ns, --ps) + if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than ('>') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '>'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]); + _cimg_mp_scalar2(mp_gt,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '<<'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) + _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((long)mem[arg1]<<(unsigned int)mem[arg2]); + _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '>>'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) + _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((long)mem[arg1]>>(unsigned int)mem[arg2]); + _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Addition ('+') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '+'",3,_cimg_mp_vector_size(arg1)); + 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 (arg2==1) _cimg_mp_scalar1(mp_increment,arg1); + if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2); + _cimg_mp_scalar2(mp_add,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Subtraction ('-') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '-'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _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 (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1); + _cimg_mp_scalar2(mp_sub,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex/matrix multiplication ('**') + s_op = "Operator '**'"; + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + if (_cimg_mp_vector_size(arg1)==2 && _cimg_mp_vector_size(arg2)==2) { // Complex multiplication + pos = vector(2); + CImg::vector((uptrT)mp_complex_mul,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); + } else { // Matrix multiplication + p1 = _cimg_mp_vector_size(arg1); + p2 = _cimg_mp_vector_size(arg2); + arg4 = p1/p2; + if (arg4*p2!=p1) { + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Sizes of left-hand and right-hand operands " + "('%s' and '%s') do not match, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + pos = vector(arg4); + CImg::vector((uptrT)mp_matrix_mul,pos,arg1,arg2,arg4,p2,1).move_to(code); + _cimg_mp_return(pos); + } } - if (*ss=='y') { // ym - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[12]!=~0U) _cimg_mp_return(reserved_label[12]); _cimg_mp_opcode0(mp_ym); + 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]); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') + s_op = "Operator '//'"; + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg1,1,s_op,3,2); + _cimg_mp_check_type(arg2,2,s_op,3,2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((uptrT)mp_complex_div_vv,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); } - if (*ss=='z') { // zm - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[13]!=~0U) _cimg_mp_return(reserved_label[13]); _cimg_mp_opcode0(mp_zm); + 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((uptrT)mp_complex_div_sv,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); } - if (*ss=='c') { // cm - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[14]!=~0U) _cimg_mp_return(reserved_label[14]); _cimg_mp_opcode0(mp_cm); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '*'",3,_cimg_mp_vector_size(arg1)); + 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]); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + + for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '/'",3,_cimg_mp_vector_size(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]); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2, ns = se1; s>ss; --s, --ns) + if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '%'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); + _cimg_mp_scalar2(mp_modulo,arg1,arg2); + } + + if (se1>ss) { + if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) // Unary plus ('+') + _cimg_mp_return(compile(ss1,se,depth1,0)); + + if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') + arg1 = compile(ss1,se,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); + _cimg_mp_scalar1(mp_minus,arg1); + } + + if (*ss=='!') { // Logical not ('!') + arg1 = compile(ss1,se,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); + _cimg_mp_scalar1(mp_logical_not,arg1); + } + + if (*ss=='~') { // Bitwise not ('~') + arg1 = compile(ss1,se,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned long)mem[arg1]); + _cimg_mp_scalar1(mp_bitwise_not,arg1); + } + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') + s_op = "Operator '^^'"; + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 2,se,depth1,0); + _cimg_mp_check_type(arg1,1,s_op,3,2); + _cimg_mp_check_type(arg2,2,s_op,3,2); + pos = (_cimg_mp_is_vector(arg1) || _cimg_mp_is_vector(arg2))?vector(2):0; + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + CImg::vector((uptrT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); } - } - if (*ss1=='M') { - if (*ss=='x') { // xM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[15]!=~0U) _cimg_mp_return(reserved_label[15]); _cimg_mp_opcode0(mp_xM); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + CImg::vector((uptrT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); } - if (*ss=='y') { // yM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[16]!=~0U) _cimg_mp_return(reserved_label[16]); _cimg_mp_opcode0(mp_yM); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + CImg::vector((uptrT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); } - if (*ss=='z') { // zM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[17]!=~0U) _cimg_mp_return(reserved_label[17]); _cimg_mp_opcode0(mp_zM); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); + switch (arg2) { + case 0 : _cimg_mp_return(1); + case 1 : _cimg_mp_return(arg1); + 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 : _cimg_mp_scalar2(mp_pow,arg1,arg2); } - if (*ss=='c') { // cM - if (!reference_stats) reference.get_stats().move_to(reference_stats); - if (reserved_label[18]!=~0U) _cimg_mp_return(reserved_label[18]); _cimg_mp_opcode0(mp_cM); + } + + for (s = se2; s>ss; --s) + if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') + arg1 = compile(ss,s,depth1,0); + arg2 = compile(s + 1,se,depth1,0); + _cimg_mp_check_type(arg2,2,"operator '^'",3,_cimg_mp_vector_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); + switch (arg2) { + case 0 : _cimg_mp_return(1); + case 1 : _cimg_mp_return(arg1); + 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 : _cimg_mp_scalar2(mp_pow,arg1,arg2); } } - } else if (ss3==se) { // Three-chars variable. - if (*ss=='w' && *ss1=='h' && *ss2=='d') _cimg_mp_return(reserved_label[1]); // whd - } else if (ss4==se) { // Four-chars variable. - if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') _cimg_mp_return(reserved_label[2]); // whds - } - // Look for variable declarations. - for (char *s = se2; s>ss; --s) - if (*s==';' && level[s - expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s + 1,se)); } - for (char *s = ss1, *ps = ss, *ns = ss2; s variable_name(ss,(unsigned int)(s - ss + 1)); - variable_name.back() = 0; - cimg::strpare(variable_name); - bool is_valid_name = true; - if (*variable_name>='0' && *variable_name<='9') is_valid_name = false; - else for (const char *ns = variable_name._data + 1; *ns; ++ns) - if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') { - is_valid_name = false; break; - } - if (!is_valid_name) { - *se = saved_char; - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Invalid variable name '%s' in specified expression " - "'%s%s%s'.", - pixel_type(),calling_function, - variable_name._data, - (ss - 8)>expr._data?"...":"", - (ss - 8)>expr._data?ss - 8:expr._data, - se<&expr.back()?"...":""); + is_sth = ss1ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment + if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) { op = mp_self_increment; s_op = "Operator '++'"; } + else { op = mp_self_decrement; s_op = "Operator '--'"; } + + ref.assign(7); + arg1 = is_sth?compile(ss2,se,depth1,ref):compile(ss,se2,depth1,ref); // Variable slot + + if (*ref>0 && !_cimg_mp_is_temp(arg1)) { // Apply operator on a copy if necessary. + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (is_sth) pos = arg1; // Determine return indice, depending on pre/post action + else { + if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1); + else pos = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k]++ + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)op,arg1,1).move_to(code); + CImg::vector((uptrT)mp_vector_set_off,arg1,arg3,(uptrT)_cimg_mp_vector_size(arg3),arg4,arg1). + move_to(code); + _cimg_mp_return(pos); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); } - const unsigned int pos = compile(s + 1,se); - - // Check for particular case of a reserved variable. - if (variable_name[1] && !variable_name[2]) { // Two-chars variable. - const char c1 = variable_name[0], c2 = variable_name[1]; - if (c1=='w' && c2=='h') variable_name.fill((char)0,(char)0); // wh - else if (c1=='p' && c2=='i') variable_name.fill(3,0); // pi - else if (c1=='i') { - if (c2=='m') variable_name.fill(4,0); // im - else if (c2=='M') variable_name.fill(5,0); // iM - else if (c2=='a') variable_name.fill(6,0); // ia - else if (c2=='v') variable_name.fill(7,0); // iv - else if (c2=='s') variable_name.fill(8,0); // is - else if (c2=='p') variable_name.fill(9,0); // ip - else if (c2=='c') variable_name.fill(10,0); // ic - } else if (c2=='m') { - if (c1=='x') variable_name.fill(11,0); // xm - else if (c1=='y') variable_name.fill(12,0); // ym - else if (c1=='z') variable_name.fill(13,0); // zm - else if (c1=='c') variable_name.fill(14,0); // cm - } else if (c2=='M') { - if (c1=='x') variable_name.fill(15,0); // xM - else if (c1=='y') variable_name.fill(16,0); // yM - else if (c1=='z') variable_name.fill(17,0); // zM - else if (c1=='c') variable_name.fill(18,0); // cM - } - } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable. - const char c1 = variable_name[0], c2 = variable_name[1], c3 = variable_name[2]; - if (c1=='w' && c2=='h' && c3=='d') variable_name.fill(1,0,0); // whd - } else if (variable_name[1] && variable_name[2] && variable_name[3] && - !variable_name[4]) { // Four-chars variable. - const char c1 = variable_name[0], c2 = variable_name[1], c3 = variable_name[2], - c4 = variable_name[3]; - if (c1=='w' && c2=='h' && c3=='d' && c4=='s') variable_name.fill(2,0,0,0); // whds + _cimg_mp_return(pos); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)mp_self_map_vector_s,arg1,(uptrT)_cimg_mp_vector_size(arg1), + (uptrT)(op==mp_self_increment?mp_self_add:mp_self_sub),1). + move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3).move_to(code); } + _cimg_mp_return(pos); + } - // Set new variable value. - if (!variable_name[1]) reserved_label[*variable_name] = pos; - else { - int label_pos = -1; - cimglist_for(labelM,i) // Check for existing variable with same name. - if (!std::strcmp(variable_name,labelM[i])) { label_pos = i; break; } - if (label_pos<0) { // If new variable. - if (labelM._width>=labelMpos._width) labelMpos.resize(-200,1,1,1,0); - label_pos = labelM.width(); - variable_name.move_to(labelM); - } - labelMpos[label_pos] = pos; + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((uptrT)mp_self_map_vector_s,arg1,(uptrT)_cimg_mp_vector_size(arg1), + (uptrT)(op==mp_self_increment?mp_self_add:mp_self_sub),1). + move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((uptrT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5).move_to(code); } _cimg_mp_return(pos); } - // Look for unary/binary operators. The operator precedences is defined as in C++. - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { - const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s + 2,se); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_logical_or),pos,mem_A,mem_B,code._width - bp1).move_to(code,bp1); + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++ + CImg::vector((uptrT)mp_self_map_vector_s,arg1,(uptrT)_cimg_mp_vector_size(arg1), + (uptrT)(op==mp_self_increment?mp_self_add:mp_self_sub),1). + move_to(code); _cimg_mp_return(pos); } - for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { - const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s + 2,se); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_logical_and),pos,mem_A,mem_B,code._width - bp1).move_to(code,bp1); + + if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++ + CImg::vector((uptrT)op,arg1).move_to(code); _cimg_mp_return(pos); } - for (char *s = se2; s>ss; --s) - if (*s=='|' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_bitwise_or,compile(ss,s),compile(s + 1,se)); - for (char *s = se2; s>ss; --s) - if (*s=='&' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_bitwise_and,compile(ss,s),compile(s + 1,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) - if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_noteq,compile(ss,s),compile(s + 2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) - if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_eqeq,compile(ss,s),compile(s + 2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) - if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_infeq,compile(ss,s),compile(s + 2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) - if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_supeq,compile(ss,s),compile(s + 2,se)); - for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps) - if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_inf,compile(ss,s),compile(s + 1,se)); - for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps) - if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_sup,compile(ss,s),compile(s + 1,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) - if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_lsl,compile(ss,s),compile(s + 2,se)); - for (char *s = se3, *ns = se2; s>ss; --s, --ns) - if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_lsr,compile(ss,s),compile(s + 2,se)); - for (char *s = se2, *ps = se3; s>ss; --s, --ps) - if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && - *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && - (*ps!='e' || !(ps>ss && (*(ps - 1)=='.' || (*(ps - 1)>='0' && *(ps - 1)<='9')))) && - level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_add,compile(ss,s),compile(s + 1,se)); - for (char *s = se2, *ps = se3; s>ss; --s, --ps) - if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && - *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && - (*ps!='e' || !(ps>ss && (*(ps - 1)=='.' || (*(ps - 1)>='0' && *(ps - 1)<='9')))) && - level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_sub,compile(ss,s),compile(s + 1,se)); - for (char *s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { - const unsigned int mem_A = compile(ss,s), bp1 = code._width, mem_B = compile(s + 1,se); - if (mempos>=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_mul),pos,mem_A,mem_B,code._width - bp1).move_to(code,bp1); + + if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1)); + else variable_name.assign(ss,(unsigned int)(se1 - ss)); + variable_name.back() = 0; + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Invalid operand '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + + // Array-like access to vectors and image values 'i/j[_#ind,offset,_boundary]' and 'vector[offset]'. + if (*se1==']' && *ss!='[') { + s_op = "Operator '[]'"; + is_relative = *ss=='j' || *ss=='J'; + + if ((*ss=='I' || *ss=='J') && *ss1=='[' && + (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a vector + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0::vector((uptrT)(is_relative?mp_list_Joff:mp_list_Ioff), + pos,p1,arg1,arg2==~0U?reserved_label[30]:arg2).move_to(code); + } else { + CImg::vector((uptrT)(is_relative?mp_Joff:mp_Ioff), + pos,arg1,arg2==~0U?reserved_label[30]:arg2).move_to(code); + } _cimg_mp_return(pos); } - for (char *s = se2; s>ss; --s) - if (*s=='/' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_div,compile(ss,s),compile(s + 1,se)); - for (char *s = se2, *ns = se1; s>ss; --s, --ns) - if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_modulo,compile(ss,s),compile(s + 1,se)); - if (ssss; --s) - if (*s=='^' && level[s - expr._data]==clevel) - _cimg_mp_opcode2(mp_pow,compile(ss,s),compile(s + 1,se)); - // Array-like access to image values 'i[]' and 'j[]'. - if (*se1==']') { - const bool is_relative = *ss=='j'; - if ((*ss=='i' || is_relative) && *ss1=='[') { - if (*ss2==']') _cimg_mp_opcode0(mp_i); - _cimg_mp_opcode1(is_relative?mp_joff:mp_ioff,compile(ss2,se1)); + if ((*ss=='i' || *ss=='j') && *ss1=='[' && + (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0ss && *s0!='[') --s0; + if (s0>ss) { // Vector value + arg1 = compile(ss,s0,depth1,0); + s1 = s0 + 1; while (s1 sub-vector extraction + arg2 = compile(++s0,s1,depth1,0); + arg3 = compile(++s1,se1,depth1,0); + _cimg_mp_check_constant(arg2,1,s_op,false); + _cimg_mp_check_constant(arg3,2,s_op,false); + p1 = (unsigned int)mem[arg2]; + p2 = (unsigned int)mem[arg3]; + p3 = _cimg_mp_vector_size(arg1); + if (p1>=p3 || p2>=p3) { + variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Out-of-bounds request for sub-vector '%s[%d,%d]' " + "(vector '%s' has dimension %u), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data,(int)mem[arg2],(int)mem[arg3], + variable_name._data,p3, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + if (p1>p2) cimg::swap(p1,p2); + (p2-=p1)++; + pos = vector(p2); + CImg::vector((uptrT)mp_vector_crop,pos,arg1,p1,p2).move_to(code); + _cimg_mp_return(pos); + } + + // One argument -> vector value reference + if (_cimg_mp_is_scalar(arg1)) { + variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + + arg2 = compile(++s0,se1,depth1,0); + if (_cimg_mp_is_constant(arg2)) { // Constant index + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_vector_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); + variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " + "(vector '%s' has dimension %u), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data,nb, + variable_name._data,_cimg_mp_vector_size(arg1), + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization + } + _cimg_mp_scalar3(mp_vector_off,arg1,(uptrT)_cimg_mp_vector_size(arg1),arg2); } } - // Look for a function call or a parenthesis. + // Look for a function call, an access to image value, or a parenthesis. if (*se1==')') { - if (*ss=='(') _cimg_mp_return(compile(ss1,se1)); - - const bool is_relative = *ss=='j'; - if ((*ss=='i' || is_relative) && *ss1=='(') { - if (*ss2==')') _cimg_mp_opcode0(mp_i); - unsigned int - indx = is_relative?0U:16U, indy = is_relative?0U:17U, - indz = is_relative?0U:18U, indc = is_relative?0U:19U, - borders = 0, interpolation = 0; - if (ss2!=se1) { - char *s1 = ss2; while (s11) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + if (s1=mem._width) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_if),pos,mem_cond,mem_A,mem_B,bp2 - bp1,code._width - bp2). - move_to(code,bp1); + if (p_ref && arg4==~0U && arg5==~0U) { + *p_ref = 5; + p_ref[1] = p1; + p_ref[2] = (unsigned int)is_relative; + p_ref[3] = arg1; + p_ref[4] = arg2; + p_ref[5] = arg3; + if (p1!=~0U && _cimg_mp_is_temp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization + if (_cimg_mp_is_temp(arg1)) memtype[arg1] = -1; + if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; + if (_cimg_mp_is_temp(arg3)) memtype[arg3] = -1; + } + p2 = ~0U; // 'p2' must the dimension of the vector-valued operand if any + if (p1==~0U) p2 = imgin._spectrum; + else if (_cimg_mp_is_constant(p1)) { + p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); + p2 = listin[p3]._spectrum; + } + _cimg_mp_check_vector0(p2,"operator '()'"); + pos = vector(p2); + if (p1!=~0U) + CImg::vector((uptrT)(is_relative?mp_list_Jxyz:mp_list_Ixyz), + pos,p1,arg1,arg2,arg3, + arg4==~0U?reserved_label[29]:arg4, + arg5==~0U?reserved_label[30]:arg5).move_to(code); + else + CImg::vector((uptrT)(is_relative?mp_Jxyz:mp_Ixyz), + pos,arg1,arg2,arg3, + arg4==~0U?reserved_label[29]:arg4, + arg5==~0U?reserved_label[30]:arg5).move_to(code); _cimg_mp_return(pos); } - if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) || - !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) || - !std::strncmp(ss,"arg(",4)) { - CImgList opcode; - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(*ss=='a'?mp_arg:*ss=='k'?mp_kth:ss[1]=='i'?mp_min: - ss[1]=='a'?mp_max:mp_med),pos). - move_to(opcode); - for (char *s = ss4; s::vector(compile(s,ns)).move_to(opcode); - s = ns; + + // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary) + if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + if (s1'y').move_to(code); - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { - unsigned int value = 0, nb = 1; - char *s1 = ss4; while (s1=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = nb_args; - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"atan2(",6)) { - char *s1 = ss6; while (s1 opcode; - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_norm),pos,(longT)(norm_type==~0U?-1:(int)norm_type)). - move_to(opcode); - for (char *s = std::strchr(ss5,'(') + 1; s::vector(compile(s,ns)).move_to(opcode); - s = ns; + if (p_ref && arg5==~0U && arg6==~0U) { + *p_ref = 3; + p_ref[1] = p1; + p_ref[2] = (unsigned int)is_relative; + p_ref[3] = arg1; + p_ref[4] = arg2; + p_ref[5] = arg3; + p_ref[6] = arg4; + if (p1!=~0U && _cimg_mp_is_temp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization + if (_cimg_mp_is_temp(arg1)) memtype[arg1] = -1; + if (_cimg_mp_is_temp(arg2)) memtype[arg2] = -1; + if (_cimg_mp_is_temp(arg3)) memtype[arg3] = -1; + if (_cimg_mp_is_temp(arg4)) memtype[arg4] = -1; + } + + if (p1!=~0U) { + if (!listin) _cimg_mp_return(0); + pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc, + p1,arg1,arg2,arg3,arg4, + arg5==~0U?reserved_label[29]:arg5, + arg6==~0U?reserved_label[30]:arg6); + } else { + if (!imgin) _cimg_mp_return(0); + pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc, + arg1,arg2,arg3,arg4, + arg5==~0U?reserved_label[29]:arg5, + arg6==~0U?reserved_label[30]:arg6); } - (opcode>'y').move_to(code); + memtype[pos] = -1; // Create it as a variable to prevent from being used in further optimization _cimg_mp_return(pos); } - if (!std::strncmp(ss,"date(",5)) { - char *s1 = ss5; while (s1=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = d; - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"fdate(",6)) { - char *s1 = ss6; while (s1::vector((uptrT)mp_complex_conj,pos,arg1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_type(arg1,0,"Function 'cexp()'",2,2); + pos = vector(2); + CImg::vector((uptrT)mp_complex_exp,pos,arg1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_type(arg1,0,"Function 'clog()'",2,2); + pos = vector(2); + CImg::vector((uptrT)mp_complex_log,pos,arg1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cos(",4)) { // Cosine + arg1 = compile(ss4,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1])); + _cimg_mp_scalar1(mp_cos,arg1); + } + + if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine + arg1 = compile(ss5,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1])); + _cimg_mp_scalar1(mp_cosh,arg1); + } + + if (!std::strncmp(ss,"cross(",6)) { // Cross product + s_op = "Function 'cross()"; + s1 = ss6; while (s1::vector((uptrT)mp_cross,pos,arg1,arg2).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cut(",4)) { // Cut + s1 = ss4; while (s1val2?val2:val); + } + _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); + } + break; + + case 'd' : + if (!std::strncmp(ss,"date(",5)) { // Date and file date + s1 = ss5; while (s1::vector((uptrT)mp_debug,arg1,code._width - p1), + CImg::string(ss6).unroll('y'))>'y').move_to(code,p1); *se1 = ')'; + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"dot(",4)) { // Dot product + s_op = "Function 'dot()'"; + s1 = ss4; while (s1::vector((uptrT)mp_dowhile,arg1,arg2,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg1); } - if (d==0 || d==1) _cimg_mp_return((unsigned int)d); - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - mem[pos] = d; - _cimg_mp_return(pos); - } + break; - // Sub-family of 'is_?()' functions. - if (*ss=='i' && *ss1=='s') { - if (!std::strncmp(ss,"isin(",5)) { - CImgList opcode; - if (mempos>=mem.size()) mem.resize(-200,1,1,1,0); - const unsigned int pos = mempos++; - CImg::vector(_cimg_mp_enfunc(mp_isin),pos).move_to(opcode); - for (char *s = ss5; s::vector(compile(s,ns)).move_to(opcode); - s = ns; + case 'e' : + if (!std::strncmp(ss,"exp(",4)) { // Exponential + arg1 = compile(ss4,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); + _cimg_mp_scalar1(mp_exp,arg1); + } + break; + + case 'f' : + if (*ss1=='o' && *ss2=='r' && (*ss3=='(' || (*ss3 && *ss3<=' ' && *ss4=='('))) { // For loop + if (*ss3<=' ') cimg::swap(*ss3,*ss4); // Allow space before opening brace + s1 = ss4; while (s1::vector((uptrT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2).move_to(code,p1); + _cimg_mp_return(pos); + } + break; + + case 'g' : + if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function + s1 = ss6; while (s10) { val/=val1; _cimg_mp_constant(val1*std::sqrt(1+val*val)); } + _cimg_mp_constant(0); } - (opcode>'y').move_to(code); + _cimg_mp_scalar2(mp_hypot,arg1,arg2); + } + break; + + case 'i' : + if (*ss1=='f' && (*ss2=='(' || (*ss2 && *ss2<=' ' && *ss3=='('))) { // If..then[..else.] + s_op = "Function 'if()'"; + if (*ss2<=' ') cimg::swap(*ss2,*ss3); // Allow space before opening brace + s1 = ss3; while (s1=se1?0:compile(s2 + 1,se1,depth1,0); + _cimg_mp_check_type(arg1,1,s_op,1,0); + _cimg_mp_check_type(arg3,3,s_op,3,_cimg_mp_vector_size(arg2)); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) + _cimg_mp_constant(mem[arg1]?mem[arg2]:mem[arg3]); + arg4 = _cimg_mp_is_vector(arg2)?_cimg_mp_vector_size(arg2):0; // Output vector size (or 0 if scalar) + if (arg4) pos = vector(arg4); else pos = scalar(); + CImg::vector((uptrT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); _cimg_mp_return(pos); } - if (!std::strncmp(ss,"isval(",6)) { - double val = 0; - if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); - _cimg_mp_return(0); + + if (!std::strncmp(ss,"init(",5)) { // Init + if (ss0!=expr._data || code.width()) { // (only allowed as the first instruction) + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: Function 'init()': Init invokation not done at the " + "beginning of expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + arg1 = compile(ss5,se1,depth1,p_ref); + init_size = code.width(); + _cimg_mp_return(arg1); } - if (!std::strncmp(ss,"isdir(",6)) { - *se1 = 0; - const bool is_dir = cimg::is_directory(ss6); - *se1 = ')'; - _cimg_mp_return(is_dir?1U:0U); + + if (!std::strncmp(ss,"int(",4)) { // Integer cast + arg1 = compile(ss4,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((long)mem[arg1]); + _cimg_mp_scalar1(mp_int,arg1); } - if (!std::strncmp(ss,"isfile(",7)) { - *se1 = 0; - const bool is_file = cimg::is_file(ss7); - *se1 = ')'; - _cimg_mp_return(is_file?1U:0U); + + if (*ss1=='s') { // Family of 'is_?()' functions + + if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? + if (ss7==se1) _cimg_mp_return(0); + arg1 = compile(ss7,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0.0 || mem[arg1]==1.0); + _cimg_mp_scalar1(mp_isbool,arg1); + } + + if (!std::strncmp(ss,"isdir(",6)) { // Is directory? + *se1 = 0; + is_sth = cimg::is_directory(ss6); + *se1 = ')'; + _cimg_mp_return(is_sth?1U:0U); + } + + if (!std::strncmp(ss,"isfile(",7)) { // Is file? + *se1 = 0; + is_sth = cimg::is_file(ss7); + *se1 = ')'; + _cimg_mp_return(is_sth?1U:0U); + } + + if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector? + pos = scalar(); + CImg::vector((uptrT)mp_isin,pos).move_to(_opcode); + for (s = ss5; s::sequence((uptrT)_cimg_mp_vector_size(arg1),arg1 + 1, + arg1 + (uptrT)_cimg_mp_vector_size(arg1)). + move_to(_opcode); + else CImg::vector(arg1).move_to(_opcode); + s = ns; + } + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); + _cimg_mp_scalar1(mp_isinf,arg1); + } + + if (!std::strncmp(ss,"isint(",6)) { // Is integer? + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::mod(mem[arg1],1.0)==0); + _cimg_mp_scalar1(mp_isint,arg1); + } + + if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); + _cimg_mp_scalar1(mp_isnan,arg1); + } + + if (!std::strncmp(ss,"isval(",6)) { // Is value? + val = 0; + if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); + _cimg_mp_return(0); + } + } - if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(mp_isnan,compile(ss6,se1)); - if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(mp_isinf,compile(ss6,se1)); - if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(mp_isint,compile(ss6,se1)); - if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(mp_isbool,compile(ss7,se1)); - } - } + break; - // No known item found, assuming this is an already initialized variable. - CImg variable_name(ss,(unsigned int)(se - ss + 1)); - variable_name.back() = 0; - if (variable_name[1]) { // Multi-char variable. - cimglist_for(labelM,i) if (!std::strcmp(variable_name,labelM[i])) _cimg_mp_return(labelMpos[i]); - } else if (reserved_label[*variable_name]!=~0U) // Single-char variable. - _cimg_mp_return(reserved_label[*variable_name]); - *se = saved_char; - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s(): Invalid item '%s' in specified expression '%s%s%s'.\n", - pixel_type(),calling_function, - variable_name._data, - (ss - 8)>expr._data?"...":"", - (ss - 8)>expr._data?ss - 8:expr._data, - se<&expr.back()?"...":""); - } + case 'l' : + if (!std::strncmp(ss,"log(",4)) { // Natural logarithm + arg1 = compile(ss4,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); + _cimg_mp_scalar1(mp_log,arg1); + } - // Evaluation functions, known by the parser. - // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulong), so we can store pointers to them - // directly in the opcode vectors. - static double mp_u(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] + cimg::rand()*(mp.mem[mp.opcode(3)] - mp.mem[mp.opcode(2)]); - } - static double mp_g(_cimg_math_parser& mp) { - cimg::unused(mp); - return cimg::grand(); - } - static double mp_i(_cimg_math_parser& mp) { - return (double)mp.reference.atXYZC((int)mp.mem[16],(int)mp.mem[17],(int)mp.mem[18],(int)mp.mem[19],0); - } - static double mp_logical_and(_cimg_math_parser& mp) { - const bool is_A = (bool)mp.mem[mp.opcode(2)]; - const CImg *const pE = ++mp.p_code + mp.opcode(4); - if (!is_A) { mp.p_code = pE - 1; return 0; } - const unsigned int mem_B = (unsigned int)mp.opcode(3); - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - --mp.p_code; - return (double)(bool)mp.mem[mem_B]; - } - static double mp_logical_or(_cimg_math_parser& mp) { - const bool is_A = (bool)mp.mem[mp.opcode(2)]; - const CImg *const pE = ++mp.p_code + mp.opcode(4); - if (is_A) { mp.p_code = pE - 1; return 1; } - const unsigned int mem_B = (unsigned int)mp.opcode(3); - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - --mp.p_code; - return (double)(bool)mp.mem[mem_B]; - } - static double mp_infeq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]<=mp.mem[mp.opcode(3)]); - } - static double mp_supeq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]>=mp.mem[mp.opcode(3)]); - } - static double mp_noteq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]!=mp.mem[mp.opcode(3)]); - } - static double mp_eqeq(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]==mp.mem[mp.opcode(3)]); - } - static double mp_inf(_cimg_math_parser& mp) { - return (double)(mp.mem[mp.opcode(2)]mp.mem[mp.opcode(3)]); - } - static double mp_add(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] + mp.mem[mp.opcode(3)]; - } - static double mp_sub(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] - mp.mem[mp.opcode(3)]; - } - static double mp_mul(_cimg_math_parser& mp) { - const double A = mp.mem[mp.opcode(2)]; - const CImg *const pE = ++mp.p_code + mp.opcode(4); - if (!A) { mp.p_code = pE - 1; return 0; } - const unsigned int mem_B = (unsigned int)mp.opcode(3); - for ( ; mp.p_code &op = *mp.p_code; - mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); + if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm + arg1 = compile(ss5,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); + _cimg_mp_scalar1(mp_log2,arg1); + } + + if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm + arg1 = compile(ss6,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); + _cimg_mp_scalar1(mp_log10,arg1); + } + break; + + case 'm' : + if (!std::strncmp(ss,"mdet(",5)) { // Matrix determinant + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_matrix_square(arg1,1,"Function 'mdet()'"); + p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + _cimg_mp_scalar2(mp_matrix_det,arg1,p1); + } + + if (!std::strncmp(ss,"mdiag(",5)) { // Diagonal matrix + arg1 = compile(ss6,se1,depth1,0); + _cimg_mp_check_type(arg1,1,"Function 'mdiag()'",2,0); + p1 = _cimg_mp_vector_size(arg1); + pos = vector(p1*p1); + CImg::vector((uptrT)mp_matrix_diag,pos,arg1,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"meig(",5)) { // Matrix eigenvalues/eigenvector + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_matrix_square(arg1,1,"Function 'meig()'"); + p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + pos = vector((p1 + 1)*p1); + CImg::vector((uptrT)mp_matrix_eig,pos,arg1,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"meye(",5)) { // Matrix eigenvalues/eigenvector + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_constant(arg1,1,"Function 'meye()'",true); + p1 = (unsigned int)mem[arg1]; + pos = vector(p1*p1); + CImg::vector((uptrT)mp_matrix_eye,pos,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"minv(",5)) { // Matrix inversion + arg1 = compile(ss5,se1,depth1,0); + _cimg_mp_check_matrix_square(arg1,1,"Function 'minv()'"); + p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + pos = vector(p1*p1); + CImg::vector((uptrT)mp_matrix_inv,pos,arg1,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"mmul(",5)) { // Matrix multiplication + s_op = "Function 'mmul()'"; + s1 = ss5; while (s1::%s: %s: Sizes of first and second arguments ('%s' and '%s') " + "do not match for third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + pos = vector(arg4*p3); + CImg::vector((uptrT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"mrot(",5)) { // Rotation matrix + s_op = "Function 'mrot()'"; + s1 = ss5; while (s1::vector((uptrT)mp_matrix_rot,pos,arg1,arg2,arg3,arg4).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"msolve(",7)) { // Solve linear system + s_op = "Function 'msolve()'"; + s1 = ss7; while (s1::%s: %s: Sizes of first and second arguments ('%s' and '%s') " + "do not match for third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + pos = vector(arg5*p3); + CImg::vector((uptrT)mp_matrix_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"mtrace(",7)) { // Matrix trace + arg1 = compile(ss7,se1,depth1,0); + _cimg_mp_check_matrix_square(arg1,1,"Function 'mtrace()'"); + p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + _cimg_mp_scalar2(mp_matrix_trace,arg1,p1); + } + + if (!std::strncmp(ss,"mtrans(",7)) { // Matrix transpose + s_op = "Function 'mtrans()'"; + s1 = ss7; while (s1::%s: %s: Size of first argument ('%s') does not match" + "for second specified argument 'nb_cols=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + pos = vector(p3*p2); + CImg::vector((uptrT)mp_matrix_trans,pos,arg1,p2,p3).move_to(code); + _cimg_mp_return(pos); + } + break; + + case 'n' : + if (!std::strncmp(ss,"narg(",5)) { // Number of arguments + if (*ss5==')') _cimg_mp_return(0); + arg1 = 0; + for (s = ss5; s::vector((uptrT)mp_norm0,pos).move_to(_opcode); break; + case 1 : CImg::vector((uptrT)mp_norm1,pos).move_to(_opcode); break; + case 2 : CImg::vector((uptrT)mp_norm2,pos).move_to(_opcode); break; + case ~0U : CImg::vector((uptrT)mp_norminf,pos).move_to(_opcode); break; + default : + CImg::vector((uptrT)mp_normp,pos,(uptrT)(arg1==~0U?-1:(int)arg1)). + move_to(_opcode); + } + for (s = std::strchr(ss5,'(') + 1; s::sequence((uptrT)_cimg_mp_vector_size(arg2),arg2 + 1, + arg2 + (uptrT)_cimg_mp_vector_size(arg2)). + move_to(_opcode); + else CImg::vector(arg2).move_to(_opcode); + s = ns; + } + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + break; + + case 'p' : + if (!std::strncmp(ss,"print(",6)) { // Print expression + pos = compile(ss6,se1,depth1,p_ref); + *se1 = 0; + if (_cimg_mp_is_vector(pos)) // Vector + ((CImg::vector((uptrT)mp_vector_print,pos,(uptrT)_cimg_mp_vector_size(pos)), + CImg::string(ss6).unroll('y'))>'y').move_to(code); + else // Scalar + ((CImg::vector((uptrT)mp_print,pos), + CImg::string(ss6).unroll('y'))>'y').move_to(code); + *se1 = ')'; + _cimg_mp_return(pos); + } + break; + + case 'r' : + if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation + s1 = ss4; while (s1::vector((uptrT)mp_vector_sort,pos,arg1,p1,arg2).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sqr(",4)) { // Square + arg1 = compile(ss4,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); + _cimg_mp_scalar1(mp_sqr,arg1); + } + + if (!std::strncmp(ss,"sqrt(",5)) { // Square root + arg1 = compile(ss5,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); + _cimg_mp_scalar1(mp_sqrt,arg1); + } + break; + + case 't' : + if (!std::strncmp(ss,"tan(",4)) { // Tangent + arg1 = compile(ss4,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); + _cimg_mp_scalar1(mp_tan,arg1); + } + + if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent + arg1 = compile(ss5,se1,depth1,0); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); + _cimg_mp_scalar1(mp_tanh,arg1); + } + break; + + case 'u' : + if (*ss1=='(') { // Random value with uniform distribution + if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); + s1 = ss2; while (s10) || + !std::strncmp(ss,"vector(",7)) { // Vector + arg2 = 0; // Number of specified values. + s = std::strchr(ss6,'(') + 1; + if (*s!=')' || arg1==~0U) for (; s::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(_opcode); + arg2+=arg4; + } else { CImg::vector(arg3).move_to(_opcode); ++arg2; } + s = ns; + } + if (arg1==~0U) arg1 = arg2; + _cimg_mp_check_vector0(arg1,"Function 'vector()'"); + pos = vector(arg1); + _opcode.insert(CImg::vector((uptrT)mp_vector_init,pos,arg1),0); + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + break; + + case 'w' : + if (!std::strncmp(ss,"whiledo",7) && (*ss7=='(' || (*ss7 && *ss7<=' ' && *ss8=='('))) { // While...do + if (*ss7<=' ') cimg::swap(*ss7,*ss8); // Allow space before opening brace + s1 = ss8; while (s1::vector((uptrT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2).move_to(code,p1); + _cimg_mp_return(pos); + } + break; + } + + if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) || + !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) || + !std::strncmp(ss,"arg(",4) || + !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7)) { // Multi-argument functions + pos = scalar(); + is_sth = *ss=='a' && ss[3]!='('; + CImg::vector((uptrT)(*ss=='a'?(ss[3]=='('?mp_arg:ss[4]=='i'?mp_argmin:mp_argmax): + *ss=='k'?mp_kth:ss[1]=='i'?mp_min: + ss[1]=='a'?mp_max:mp_med),pos). + move_to(_opcode); + for (s = is_sth?ss7:ss4; s::sequence((uptrT)_cimg_mp_vector_size(arg2),arg2 + 1, + arg2 + (uptrT)_cimg_mp_vector_size(arg2)). + move_to(_opcode); + else CImg::vector(arg2).move_to(_opcode); + s = ns; + } + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); + } + + // No corresponding built-in function -> Look for a user-defined function. + s0 = strchr(ss,'('); + if (s0) { + variable_name.assign(ss,s0 - ss + 1).back() = 0; + cimglist_for(function_def,l) if (!std::strcmp(function_def[l],variable_name)) { + p2 = (unsigned int)function_def[l].back(); // Number of required arguments + CImg _expr = function_body[l]; // Expression to be substituted + p1 = 1; // Indice of current parsed argument + for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments + while (*s && *s<=' ') ++s; + if (*s==')' && p1==1) break; // Function has no arguments + if (p1>p2) { ++p1; break; } + ns = s; while (ns::%s: function '%s()': Number of specified arguments does not " + "match function declaration (%u argument%s required), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,variable_name._data, + p2,p2!=1?"s":"", + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + + // Recompute 'pexpr' and 'level' for evaluating substituted expression. + CImg _pexpr(_expr._width); + ns = _pexpr._data; + for (ps = _expr._data, c1 = ' '; *ps; ++ps) { + if (*ps!=' ') c1 = *ps; + *(ns++) = c1; + } + *ns = 0; + + CImg _level(_expr._width - 1); + unsigned int *pd = _level._data; + nb = 0; + for (ps = _expr._data; *ps && nb>=0; ++ps) + *(pd++) = (unsigned int)(*ps=='('||*ps=='['?nb++:*ps==')'||*ps==']'?--nb:nb); + + expr.swap(_expr); pexpr.swap(_pexpr); level.swap(_level); + s0 = user_function; + user_function = function_def[l]; + pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref); + user_function = s0; + expr.swap(_expr); pexpr.swap(_pexpr); level.swap(_level); + _cimg_mp_return(pos); + } + } + } // if (se1==')') + + // Vector specification using initializer '[ ... ]'. + if (*ss=='[' && *se1==']') { + arg1 = 0; // Number of specified values. + if (*ss1!=']') for (s = ss1; s::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(_opcode); + arg1+=arg3; + } else { CImg::vector(arg2).move_to(_opcode); ++arg1; } + s = ns; + } + _cimg_mp_check_vector0(arg1,"operator '[]'"); + pos = vector(arg1); + _opcode.insert(CImg::vector((uptrT)mp_vector_init,pos,arg1),0); + (_opcode>'y').move_to(code); + _cimg_mp_return(pos); } - --mp.p_code; - return A*(double)mp.mem[mem_B]; + + // Variables related to the input list of images. + if (*ss1=='#' && ss2::vector(listin[p1].median()).move_to(list_median[p1]); + _cimg_mp_constant(*list_median[p1]); + } + _cimg_mp_scalar1(mp_list_median,arg1); + } + if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_x,_cimg_mp_y,_cimg_mp_z,*ss1 - '0', + reserved_label[29],reserved_label[30]); + } + switch (*ss1) { + case 'm' : arg2 = 0; break; // im#ind + case 'M' : arg2 = 1; break; // iM#ind + case 'a' : arg2 = 2; break; // ia#ind + case 'v' : arg2 = 3; break; // iv#ind + case 's' : arg2 = 12; break; // is#ind + case 'p' : arg2 = 13; break; // ip#ind + } + } else if (*ss1=='m') switch (*ss) { + case 'x' : arg2 = 4; break; // xm#ind + case 'y' : arg2 = 5; break; // ym#ind + case 'z' : arg2 = 6; break; // zm#ind + case 'c' : arg2 = 7; break; // cm#ind + } else if (*ss1=='M') switch (*ss) { + case 'x' : arg2 = 8; break; // xM#ind + case 'y' : arg2 = 9; break; // yM#ind + case 'z' : arg2 = 10; break; // zM#ind + case 'c' : arg2 = 11; break; // cM#ind + } + if (arg2!=~0U) { + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_stats) list_stats.assign(listin._width); + if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false); + _cimg_mp_constant(list_stats(p1,arg2)); + } + _cimg_mp_scalar2(mp_list_stats,arg1,arg2); + } + } + + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4 error. + is_sth = true; // is_valid_variable_name + if (*variable_name>='0' && *variable_name<='9') is_sth = false; + else for (ns = variable_name._data + 1; *ns; ++ns) + if (!is_varchar(*ns)) { is_sth = false; break; } + + *se = saved_char; cimg::strellipsize(variable_name,64); cimg::strellipsize(expr,64); + if (is_sth) + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + s0 = std::strchr(ss,'('); + if (s0 && *se1==')') s_op = "function call"; else s_op = "item"; + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + s_op,variable_name._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); } - static double mp_div(_cimg_math_parser& mp) { - return mp.mem[mp.opcode(2)] / mp.mem[mp.opcode(3)]; + + // Evaluation procedure. + double operator()(const double x, const double y, const double z, const double c) { + mem[_cimg_mp_x] = x; mem[_cimg_mp_y] = y; mem[_cimg_mp_z] = z; mem[_cimg_mp_c] = c; + for (p_code = p_code_begin; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + const uptrT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + return *result; } - static double mp_minus(_cimg_math_parser& mp) { - return -mp.mem[mp.opcode(2)]; + + // Evaluation procedure (return output values in vector 'output'). + template + void operator()(const double x, const double y, const double z, const double c, t *const output) { + mem[_cimg_mp_x] = x; mem[_cimg_mp_y] = y; mem[_cimg_mp_z] = z; mem[_cimg_mp_c] = c; + for (p_code = p_code_begin; p_code &op = *p_code; + opcode._data = op._data; opcode._height = op._height; + const uptrT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + if (result_dim) { + const double *ptrs = result + 1; + t *ptrd = output; + for (unsigned int k = 0; k s_type(const unsigned int arg) const { + CImg res; + if (_cimg_mp_is_vector(arg)) { // Vector + CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); + std::sprintf(res._data + 6,"%u",_cimg_mp_vector_size(arg)); + } else CImg::string("scalar").move_to(res); + return res; } - static double mp_bitwise_not(_cimg_math_parser& mp) { - return ~(unsigned long)mp.mem[mp.opcode(2)]; + + // Insert constant value in memory. + unsigned int constant(const double val) { + if (val==(double)(int)val) { + if (val>=0 && val<=9) return (unsigned int)val; + if (val<0 && val>=-5) return (unsigned int)(10 - val); + } + if (val==0.5) return 16; + if (cimg::type::is_nan(val)) return 28; + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); } + const unsigned int pos = mempos++; + mem[pos] = val; + memtype[pos] = 1; // Set constant property + return pos; } - static double mp_modulo(_cimg_math_parser& mp) { - return cimg::mod(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)]); + + // Insert code instructions for processing scalars. + unsigned int scalar() { // Insert new scalar in memory. + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); } + return mempos++; } - static double mp_bitwise_and(_cimg_math_parser& mp) { - return ((unsigned long)mp.mem[mp.opcode(2)] & (unsigned long)mp.mem[mp.opcode(3)]); + + unsigned int scalar0(const mp_func op) { + const unsigned int pos = scalar(); + CImg::vector((uptrT)op,pos).move_to(code); + return pos; } - static double mp_bitwise_or(_cimg_math_parser& mp) { - return ((unsigned long)mp.mem[mp.opcode(2)] | (unsigned long)mp.mem[mp.opcode(3)]); + + unsigned int scalar1(const mp_func op, const unsigned int arg1) { + const unsigned int pos = + arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1:scalar(); + CImg::vector((uptrT)op,pos,arg1).move_to(code); + return pos; } - static double mp_pow(_cimg_math_parser& mp) { - const double v = mp.mem[mp.opcode(2)], p = mp.mem[mp.opcode(3)]; - if (p==0) return 1; - if (p==0.5) return std::sqrt(v); - if (p==1) return v; - if (p==2) return v*v; - if (p==3) return v*v*v; - if (p==4) return v*v*v*v; - return std::pow(v,p); + + unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int pos = + arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: + arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2:scalar(); + CImg::vector((uptrT)op,pos,arg1,arg2).move_to(code); + return pos; } - static double mp_sin(_cimg_math_parser& mp) { - return std::sin(mp.mem[mp.opcode(2)]); + + unsigned int scalar3(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { + const unsigned int pos = + arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: + arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2: + arg3>_cimg_mp_c && _cimg_mp_is_temp(arg3)?arg3:scalar(); + CImg::vector((uptrT)op,pos,arg1,arg2,arg3).move_to(code); + return pos; } - static double mp_cos(_cimg_math_parser& mp) { - return std::cos(mp.mem[mp.opcode(2)]); + + unsigned int scalar6(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) { + const unsigned int pos = + arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: + arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2: + arg3>_cimg_mp_c && _cimg_mp_is_temp(arg3)?arg3: + arg4>_cimg_mp_c && _cimg_mp_is_temp(arg4)?arg4: + arg5>_cimg_mp_c && _cimg_mp_is_temp(arg5)?arg5: + arg6>_cimg_mp_c && _cimg_mp_is_temp(arg6)?arg6:scalar(); + CImg::vector((uptrT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); + return pos; } - static double mp_tan(_cimg_math_parser& mp) { - return std::tan(mp.mem[mp.opcode(2)]); + + unsigned int scalar7(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6, + const unsigned int arg7) { + const unsigned int pos = + arg1>_cimg_mp_c && _cimg_mp_is_temp(arg1)?arg1: + arg2>_cimg_mp_c && _cimg_mp_is_temp(arg2)?arg2: + arg3>_cimg_mp_c && _cimg_mp_is_temp(arg3)?arg3: + arg4>_cimg_mp_c && _cimg_mp_is_temp(arg4)?arg4: + arg5>_cimg_mp_c && _cimg_mp_is_temp(arg5)?arg5: + arg6>_cimg_mp_c && _cimg_mp_is_temp(arg6)?arg6: + arg7>_cimg_mp_c && _cimg_mp_is_temp(arg7)?arg7:scalar(); + CImg::vector((uptrT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); + return pos; } - static double mp_asin(_cimg_math_parser& mp) { - return std::asin(mp.mem[mp.opcode(2)]); + + // Return a string that defines the calling function + the user-defined function scope. + CImg calling_function_s() const { + CImg res; + const unsigned int + l1 = calling_function?std::strlen(calling_function):0, + l2 = user_function?std::strlen(user_function):0; + if (l2) { + res.assign(l1 + l2 + 48); + cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_function); + } else { + res.assign(l1 + l2 + 4); + cimg_snprintf(res,res._width,"%s()",calling_function); + } + return res; } + + // Return true if specified argument can be a part of an allowed variable name. + bool is_varchar(const char c) const { + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; + } + + // Insert code instructions for processing vectors. + bool is_tmp_vector(const unsigned int arg) const { + unsigned int siz = _cimg_mp_vector_size(arg); + if (siz>8) return false; + const int *ptr = memtype.data(arg + 1); + bool is_tmp = true; + while (siz-->0) if (*(ptr++)) { is_tmp = false; break; } + return is_tmp; + } + + void set_variable_vector(const unsigned int arg) { + unsigned int siz = _cimg_mp_vector_size(arg); + int *ptr = memtype.data(arg + 1); + while (siz-->0) *(ptr++) = -1; + } + + unsigned int vector(const unsigned int siz) { // Insert new vector of specified size in memory. + if (mempos + siz>=mem._width) { + mem.resize(2*mem._width + siz,1,1,1,0); + memtype.resize(mem._width,1,1,1,0); + } + const unsigned int pos = mempos++; + mem[pos] = cimg::type::nan(); + memtype[pos] = siz + 1; + mempos+=siz; + return pos; + } + + unsigned int vector_copy(const unsigned int arg) { // Insert new copy of specified vector in memory. + const unsigned int + siz = _cimg_mp_vector_size(arg), + pos = vector(siz); + CImg::vector((uptrT)mp_vector_copy,pos,arg,siz).move_to(code); + return pos; + } + + unsigned int vector1_v(const mp_func op, const unsigned int arg1) { + const unsigned int + siz = _cimg_mp_vector_size(arg1), + pos = is_tmp_vector(arg1)?arg1:vector(siz); + CImg::vector((uptrT)mp_vector_map_v,pos,siz,(uptrT)op,arg1).move_to(code); + return pos; + } + + unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_vector_size(arg1), + pos = is_tmp_vector(arg1)?arg1:is_tmp_vector(arg2)?arg2:vector(siz); + CImg::vector((uptrT)mp_vector_map_vv,pos,siz,(uptrT)op,arg1,arg2).move_to(code); + return pos; + } + + unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_vector_size(arg1), + pos = is_tmp_vector(arg1)?arg1:vector(siz); + CImg::vector((uptrT)mp_vector_map_vs,pos,siz,(uptrT)op,arg1,arg2).move_to(code); + return pos; + } + + unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_vector_size(arg2), + pos = is_tmp_vector(arg2)?arg2:vector(siz); + CImg::vector((uptrT)mp_vector_map_sv,pos,siz,(uptrT)op,arg1,arg2).move_to(code); + return pos; + } + + unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2, + const unsigned int arg3) { + const unsigned int + siz = _cimg_mp_vector_size(arg1), + pos = is_tmp_vector(arg1)?arg1:vector(siz); + CImg::vector((uptrT)mp_vector_map_vss,pos,siz,(uptrT)op,arg1,arg2,arg3).move_to(code); + return pos; + } + + // Check if a memory slot is a positive integer constant scalar value. + void check_constant(const unsigned int arg, const unsigned int n_arg, const char *const s_op, + const bool is_strictly_positive, + const char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,s_op,1,0); + if (!_cimg_mp_is_constant(arg) || mem[arg]<(is_strictly_positive?1:0) || (double)(int)mem[arg]!=mem[arg]) { + const char *s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One "; + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): %s: %s%s (of type '%s') is not a %spositive integer constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_arg,*s_arg?"argument":"Argument",s_type(arg)._data, + is_strictly_positive?"strictly ":"", + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + } + + // Check a matrix is square. + void check_matrix_square(const unsigned int arg, const unsigned int n_arg, const char *const s_op, + const char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,s_op,2,0); + const unsigned int + siz = _cimg_mp_vector_size(arg), + n = (unsigned int)std::sqrt((float)siz); + if (n*n!=siz) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand "; + else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One "; + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): %s: %s%s (of type '%s') " + "cannot be considered as a square matrix, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"), + s_type(arg)._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + } + + // Check type compatibility for one argument. + // Bits of 'mode' tells what types are allowed: + // { 1 = scalar | 2 = vectorN }. + // If 'N' is not zero, it also restricts the vectors to be of size N only. + void check_type(const unsigned int arg, const unsigned int n_arg, const char *const s_op, + const unsigned int mode, const unsigned int N, + const char *const ss, char *const se, const char saved_char) { + const bool + is_scalar = _cimg_mp_is_scalar(arg), + is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_vector_size(arg)==N); + bool cond = false; + if (mode&1) cond|=is_scalar; + if (mode&2) cond|=is_vector; + if (!cond) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand "; + else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One "; + CImg sb_type(32); + if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'"); + else if (mode==2) { + if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'vector'"); + } else { + if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'"); + } + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): %s: %s%s has invalid type '%s' (should be %s), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"), + s_type(arg)._data,sb_type._data, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + } + + // Check a vector is not 0-dimensional, or with unknown dimension at compile time. + void check_vector0(const unsigned int dim, const char *const s_op, + const char *const ss, char *const se, const char saved_char) { + if (!dim) { + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): %s: Invalid construction of a 0-dimensional vector, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } else if (dim==~0U) { + *se = saved_char; cimg::strellipsize(expr,64); + throw CImgArgumentException("[_cimg_math_parser] " + "CImg<%s>::%s(): %s: Invalid construction of a vector with dynamic size, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + } + + // Evaluation functions, known by the parser. + // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(uptrT), + // so we can store pointers to them directly in the opcode vectors. +#ifdef _mp_arg +#undef _mp_arg +#endif +#define _mp_arg(x) mp.mem[mp.opcode[x]] + + static double mp_abs(_cimg_math_parser& mp) { + return cimg::abs(_mp_arg(2)); + } + + static double mp_add(_cimg_math_parser& mp) { + return _mp_arg(2) + _mp_arg(3); + } + static double mp_acos(_cimg_math_parser& mp) { - return std::acos(mp.mem[mp.opcode(2)]); + return std::acos(_mp_arg(2)); } - static double mp_atan(_cimg_math_parser& mp) { - return std::atan(mp.mem[mp.opcode(2)]); + + static double mp_arg(_cimg_math_parser& mp) { + const int _ind = (int)_mp_arg(2); + const unsigned int nb_args = mp.opcode._height - 2, ind = _ind<0?_ind + nb_args:(unsigned int)_ind; + if (ind>=nb_args) return 0; + return _mp_arg(ind + 2); } - static double mp_sinh(_cimg_math_parser& mp) { - return std::sinh(mp.mem[mp.opcode(2)]); + + static double mp_argmin(_cimg_math_parser& mp) { + double val = _mp_arg(2); + unsigned int argval = 0; + for (unsigned int i = 3; ival) { val = _val; argval = i - 2; } + } + return (double)argval; } - static double mp_tanh(_cimg_math_parser& mp) { - return std::tanh(mp.mem[mp.opcode(2)]); + + static double mp_asin(_cimg_math_parser& mp) { + return std::asin(_mp_arg(2)); } - static double mp_log10(_cimg_math_parser& mp) { - return std::log10(mp.mem[mp.opcode(2)]); + + static double mp_atan(_cimg_math_parser& mp) { + return std::atan(_mp_arg(2)); } - static double mp_log2(_cimg_math_parser& mp) { - return cimg::log2(mp.mem[mp.opcode(2)]); + + static double mp_atan2(_cimg_math_parser& mp) { + return std::atan2(_mp_arg(2),_mp_arg(3)); } - static double mp_log(_cimg_math_parser& mp) { - return std::log(mp.mem[mp.opcode(2)]); + + static double mp_bitwise_and(_cimg_math_parser& mp) { + return (double)((unsigned long)_mp_arg(2) & (unsigned long)_mp_arg(3)); } - static double mp_exp(_cimg_math_parser& mp) { - return std::exp(mp.mem[mp.opcode(2)]); + + static double mp_bitwise_left_shift(_cimg_math_parser& mp) { + return (double)((long)_mp_arg(2)<<(unsigned int)_mp_arg(3)); } - static double mp_sqr(_cimg_math_parser& mp) { - return cimg::sqr(mp.mem[mp.opcode(2)]); + + static double mp_bitwise_not(_cimg_math_parser& mp) { + return (double)~(unsigned long)_mp_arg(2); } - static double mp_sqrt(_cimg_math_parser& mp) { - return std::sqrt(mp.mem[mp.opcode(2)]); + + static double mp_bitwise_or(_cimg_math_parser& mp) { + return (double)((unsigned long)_mp_arg(2) | (unsigned long)_mp_arg(3)); } + + static double mp_bitwise_right_shift(_cimg_math_parser& mp) { + return (double)((long)_mp_arg(2)>>(unsigned int)_mp_arg(3)); + } + static double mp_cbrt(_cimg_math_parser& mp) { - return std::pow(mp.mem[mp.opcode(2)],1.0/3); + return std::pow(_mp_arg(2),1.0/3); } - static double mp_hypot(_cimg_math_parser& mp) { - double - x = cimg::abs(mp.mem[mp.opcode(2)]), - y = cimg::abs(mp.mem[mp.opcode(3)]), - t; - if (x0) { t/=x; return x*std::sqrt(1+t*t); } - return 0; + + static double mp_complex_conj(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + double *ptrd = &_mp_arg(1) + 1; + *(ptrd++) = *(ptrs++); + *ptrd = -*(ptrs); + return cimg::type::nan(); } - static double mp_norm(_cimg_math_parser& mp) { - const unsigned int norm_type = (unsigned int)mp.opcode(2); - double res = 0; - switch (norm_type) { - case 0 : // L0-norm. - for (unsigned int i = 3; ires) res = val; - } - break; - default: // Lp-norm. - for (unsigned int i = 3; i::nan(); + } + + static double mp_complex_div_vv(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + const double denom = r2*r2 + i2*i2; + *(ptrd++) = (r1*r2 + i1*i2)/denom; + *ptrd = (r2*i1 - r1*i2)/denom; + return cimg::type::nan(); + } + + static double mp_complex_exp(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs), er = std::exp(r); + *(ptrd++) = er*std::cos(i); + *(ptrd++) = er*std::sin(i); + return cimg::type::nan(); + } + + static double mp_complex_log(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs); + *(ptrd++) = std::log(std::sqrt(r*r + i*i)); + *(ptrd++) = std::atan2(i,r); + return cimg::type::nan(); + } + + static double mp_complex_mul(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + *(ptrd++) = r1*r2 - i1*i2; + *(ptrd++) = r1*i2 + r2*i1; + return cimg::type::nan(); + } + + static void _mp_complex_pow(const double r1, const double i1, + const double r2, const double i2, + double *ptrd) { + double ro, io; + if (cimg::abs(i2)<1e-15) { // Exponent is real + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) { + if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; } + else ro = io = 0; + } else { + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2), + phio = r2*phi1; + ro = modo*std::cos(phio); + io = modo*std::sin(phio); + } + } else { // Exponent is complex + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0; + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1), + phio = r2*phi1 + 0.5*i2*std::log(mod1_2); + ro = modo*std::cos(phio); + io = modo*std::sin(phio); } - return res>0?res:0.0; + *(ptrd++) = ro; + *ptrd = io; } - static double mp_sign(_cimg_math_parser& mp) { - return cimg::sign(mp.mem[mp.opcode(2)]); + + static double mp_complex_pow_sv(_cimg_math_parser& mp) { + const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vs(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vv(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_cos(_cimg_math_parser& mp) { + return std::cos(_mp_arg(2)); } - static double mp_time(_cimg_math_parser& mp) { + + static double mp_cosh(_cimg_math_parser& mp) { + return std::cosh(_mp_arg(2)); + } + + static double mp_cross(_cimg_math_parser& mp) { + CImg + vout(&_mp_arg(1) + 1,1,3,1,1,true), + v1(&_mp_arg(2) + 1,1,3,1,1,true), + v2(&_mp_arg(3) + 1,1,3,1,1,true); + (vout = v1).cross(v2); + return cimg::type::nan(); + } + + static double mp_cut(_cimg_math_parser& mp) { + double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4); + return valcmax?cmax:val; + } + + static double mp_debug(_cimg_math_parser& mp) { +#ifdef cimg_use_openmp + const unsigned int n_thread = omp_get_thread_num(); +#else + const unsigned int n_thread = 0; +#endif + CImg expr(mp.opcode._height - 3); + const uptrT *ptrs = mp.opcode._data + 3; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(expr); + const uptrT g_target = mp.opcode[1]; + std::fprintf(cimg::output(), + "\n[_cimg_math_parser] %p[thread #%u]:%*c" + "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)mp.opcode[2],(unsigned int)g_target,mp.mem._width); + std::fflush(cimg::output()); + const CImg *const p_end = (++mp.p_code) + mp.opcode[2]; + CImg _op; + mp.debug_indent+=3; + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + + _op.assign(1,op._height - 1); + const uptrT *ptrs = op._data + 1; + for (uptrT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd mem[%u] = %g", + (void*)&mp,n_thread,mp.debug_indent,' ', + (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(), + (unsigned int)target,mp.mem[target]); + std::fflush(cimg::output()); + } + mp.debug_indent-=3; + std::fprintf(cimg::output(), + "\n[_cimg_math_parser] %p[thread #%u]:%*c" + "End debugging expression '%s' -> mem[%u] = %g (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width); + std::fflush(cimg::output()); + --mp.p_code; + return mp.mem[g_target]; + } + + static double mp_decrement(_cimg_math_parser& mp) { + return _mp_arg(2) - 1; + } + + static double mp_dot(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[4]; + return CImg(&_mp_arg(2) + 1,1,siz,1,1,true). + dot(CImg(&_mp_arg(3) + 1,1,siz,1,1,true)); + } + + static double mp_dowhile(_cimg_math_parser& mp) { + const uptrT + mem_proc = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_proc = ++mp.p_code, + *const p_end = p_proc + mp.opcode[3]; + do { + for (mp.p_code = p_proc; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + } while (mp.mem[mem_cond]); + --mp.p_code; + return mp.mem[mem_proc]; + } + + static double mp_div(_cimg_math_parser& mp) { + return _mp_arg(2)/_mp_arg(3); + } + + static double mp_eq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)==_mp_arg(3)); + } + + static double mp_exp(_cimg_math_parser& mp) { + return std::exp(_mp_arg(2)); + } + + static double mp_g(_cimg_math_parser& mp) { cimg::unused(mp); - return (double)cimg::time(); + return cimg::grand(); } - static double mp_abs(_cimg_math_parser& mp) { - return cimg::abs(mp.mem[mp.opcode(2)]); + + static double mp_gauss(_cimg_math_parser& mp) { + const double x = _mp_arg(2), s = _mp_arg(3); + return std::exp(-x*x/(2*s*s))/std::sqrt(2*s*s*cimg::PI); } - static double mp_atan2(_cimg_math_parser& mp) { - return std::atan2(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)]); + + static double mp_gt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>_mp_arg(3)); } + + static double mp_gte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>=_mp_arg(3)); + } + + static double mp_hypot(_cimg_math_parser& mp) { + return cimg::hypot(_mp_arg(2),_mp_arg(3)); + } + + static double mp_i(_cimg_math_parser& mp) { + return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_x],(int)mp.mem[_cimg_mp_y], + (int)mp.mem[_cimg_mp_z],(int)mp.mem[_cimg_mp_c],0); + } + static double mp_if(_cimg_math_parser& mp) { - const bool is_cond = (bool)mp.mem[mp.opcode(2)]; - const unsigned int mem_A = (unsigned int)mp.opcode(3), mem_B = (unsigned int)mp.opcode(4); - const CImg - *const pB = ++mp.p_code + mp.opcode(5), - *const pE = pB + mp.opcode(6); - if (is_cond) { // Evaluate on-the-fly only the correct argument. - for ( ; mp.p_code &op = *mp.p_code; + const bool is_cond = (bool)_mp_arg(2); + const uptrT + mem_left = mp.opcode[3], + mem_right = mp.opcode[4]; + const CImg + *const p_right = ++mp.p_code + mp.opcode[5], + *const p_end = p_right + mp.opcode[6]; + const unsigned int vtarget = mp.opcode[1], vsiz = mp.opcode[7]; + if (is_cond) { + for ( ; mp.p_code &op = *mp.p_code; mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; + const uptrT target = mp.opcode[1]; mp.mem[target] = _cimg_mp_defunc(mp); } - mp.p_code = pE - 1; - return mp.mem[mem_A]; + mp.p_code = p_end - 1; + if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[mem_left] + 1,sizeof(double)*vsiz); + return mp.mem[mem_left]; } - for (mp.p_code = pB; mp.p_code &op = *mp.p_code; + for (mp.p_code = p_right; mp.p_code &op = *mp.p_code; mp.opcode._data = op._data; mp.opcode._height = op._height; - const unsigned int target = (unsigned int)mp.opcode[1]; + const uptrT target = mp.opcode[1]; mp.mem[target] = _cimg_mp_defunc(mp); } --mp.p_code; - return mp.mem[mem_B]; + if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[mem_right] + 1,sizeof(double)*vsiz); + return mp.mem[mem_right]; } - static double mp_round(_cimg_math_parser& mp) { - return cimg::round(mp.mem[mp.opcode(2)],mp.mem[mp.opcode(3)],(int)mp.mem[mp.opcode(4)]); + + static double mp_increment(_cimg_math_parser& mp) { + return _mp_arg(2) + 1; + } + + static double mp_int(_cimg_math_parser& mp) { + return (double)(long)_mp_arg(2); + } + + static double mp_ioff(_cimg_math_parser& mp) { + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3); + const CImg &img = mp.imgin; + const long + off = (long)_mp_arg(2), + whds = (long)img.size(); + if (off<0 || off>=whds) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (img) return (double)img[cimg::mod(off,whds)]; + return 0; + case 1 : // Neumann boundary + if (img) return (double)(off<0?*img:img.back()); + return 0; + default : // Dirichet boundary + return 0; + } + return (double)img[off]; + } + + static double mp_isbool(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return (double)(val==0.0 || val==1.0); + } + + static double mp_isin(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + for (unsigned int i = 3; i::is_inf(_mp_arg(2)); + } + + static double mp_isint(_cimg_math_parser& mp) { + return (double)(cimg::mod(_mp_arg(2),1.0)==0); + } + + static double mp_isnan(_cimg_math_parser& mp) { + return (double)cimg::type::is_nan(_mp_arg(2)); } + static double mp_ixyzc(_cimg_math_parser& mp) { + const unsigned int + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + const CImg &img = mp.imgin; const double - x = mp.mem[mp.opcode(2)], y = mp.mem[mp.opcode(3)], z = mp.mem[mp.opcode(4)], c = mp.mem[mp.opcode(5)]; - const int i = (int)mp.mem[mp.opcode(6)], b = (int)mp.mem[mp.opcode(7)]; - if (i==0) { // Nearest neighbor interpolation. - if (b==2) return (double)mp.reference.atXYZC(cimg::mod((int)x,mp.reference.width()), - cimg::mod((int)y,mp.reference.height()), - cimg::mod((int)z,mp.reference.depth()), - cimg::mod((int)c,mp.reference.spectrum())); - if (b==1) return (double)mp.reference.atXYZC((int)x,(int)y,(int)z,(int)c); - return (double)mp.reference.atXYZC((int)x,(int)y,(int)z,(int)c,0); - } else { // Linear interpolation. - if (b==2) return (double)mp.reference.linear_atXYZC(cimg::mod((float)x,(float)mp.reference.width()), - cimg::mod((float)y,(float)mp.reference.height()), - cimg::mod((float)z,(float)mp.reference.depth()), - cimg::mod((float)c,(float)mp.reference.spectrum())); - if (b==1) return (double)mp.reference.linear_atXYZC((float)x,(float)y,(float)z,(float)c); - return (double)mp.reference.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); + x = _mp_arg(2), y = _mp_arg(3), + z = _mp_arg(4), c = _mp_arg(5); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + return (double)img.atXYZC(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + cimg::mod((int)c,img.spectrum())); + if (boundary_conditions==1) + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()), + cimg::mod((float)c,(float)img.spectrum())); + if (boundary_conditions==1) + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); } } + + static double mp_joff(_cimg_math_parser& mp) { + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3); + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const CImg &img = mp.imgin; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), + whds = (long)img.size(); + if (off<0 || off>=whds) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (img) return (double)img[cimg::mod(off,whds)]; + return 0; + case 1 : // Neumann boundary + if (img) return (double)(off<0?*img:img.back()); + return 0; + default : // Dirichet boundary + return 0; + } + return (double)img[off]; + } + static double mp_jxyzc(_cimg_math_parser& mp) { - const double x = mp.mem[16], y = mp.mem[17], z = mp.mem[18], c = mp.mem[19]; + const unsigned int + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + const CImg &img = mp.imgin; const double - dx = mp.mem[mp.opcode(2)], dy = mp.mem[mp.opcode(3)], dz = mp.mem[mp.opcode(4)], dc = mp.mem[mp.opcode(5)]; - const int i = (int)mp.mem[mp.opcode(6)], b = (int)mp.mem[mp.opcode(7)]; - if (i==0) { // Nearest neighbor interpolation. - if (b==2) return (double)mp.reference.atXYZC(cimg::mod((int)(x + dx),mp.reference.width()), - cimg::mod((int)(y + dy),mp.reference.height()), - cimg::mod((int)(z + dz),mp.reference.depth()), - cimg::mod((int)(c + dc),mp.reference.spectrum())); - if (b==1) return (double)mp.reference.atXYZC((int)(x + dx),(int)(y + dy),(int)(z + dz),(int)(c + dc)); - return (double)mp.reference.atXYZC((int)(x + dx),(int)(y + dy),(int)(z + dz),(int)(c + dc),0); - } else { // Linear interpolation. - if (b==2) - return (double)mp.reference.linear_atXYZC(cimg::mod((float)(x + dx),(float)mp.reference.width()), - cimg::mod((float)(y + dy),(float)mp.reference.height()), - cimg::mod((float)(z + dz),(float)mp.reference.depth()), - cimg::mod((float)(c + dc),(float)mp.reference.spectrum())); - if (b==1) return (double)mp.reference.linear_atXYZC((float)(x + dx),(float)(y + dy), - (float)(z + dz),(float)(c + dc)); - return (double)mp.reference.linear_atXYZC((float)(x + dx),(float)(y + dy),(float)(z + dz),(float)(c + dc),0); + ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], + oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), + z = oz + _mp_arg(4), c = oc + _mp_arg(5); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + return (double)img.atXYZC(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + cimg::mod((int)c,img.spectrum())); + if (boundary_conditions==1) + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()), + cimg::mod((float)c,(float)img.spectrum())); + if (boundary_conditions==1) + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); } } - static double mp_min(_cimg_math_parser& mp) { - double val = mp.mem[mp.opcode(2)]; - for (unsigned int i = 3; i vals(mp.opcode._height - 3); + double *p = vals.data(); + for (unsigned int i = 3; i &img = mp.listin[ind]; + const long + off = (long)_mp_arg(3), + whds = (long)img.size(); + if (off<0 || off>=whds) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (img) return (double)img[cimg::mod(off,whds)]; + return 0; + case 1 : // Neumann boundary + if (img) return (double)(off<0?*img:img.back()); + return 0; + default : // Dirichet boundary + return 0; + } + return (double)img[off]; + } + + static double mp_list_is_shared(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._is_shared; + } + + static double mp_list_ixyzc(_cimg_math_parser& mp) { + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(7), + boundary_conditions = (unsigned int)_mp_arg(8); + const CImg &img = mp.listin[ind]; + const double + x = _mp_arg(3), y = _mp_arg(4), + z = _mp_arg(5), c = _mp_arg(6); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + return (double)img.atXYZC(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + cimg::mod((int)c,img.spectrum())); + if (boundary_conditions==1) + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()), + cimg::mod((float)c,(float)img.spectrum())); + if (boundary_conditions==1) + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); + } + } + + static double mp_list_joff(_cimg_math_parser& mp) { + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4); + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const CImg &img = mp.listin[ind]; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), + whds = (long)img.size(); + if (off<0 || off>=whds) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (img) return (double)img(ind,cimg::mod(off,whds)); + return 0; + case 1 : // Neumann boundary + if (img) return (double)(off<0?*img:img.back()); + return 0; + default : // Dirichet boundary + return 0; + } + return (double)img[off]; + } + + static double mp_list_jxyzc(_cimg_math_parser& mp) { + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(7), + boundary_conditions = (unsigned int)_mp_arg(8); + const CImg &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], + oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), + z = oz + _mp_arg(5), c = oc + _mp_arg(6); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + return (double)img.atXYZC(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + cimg::mod((int)c,img.spectrum())); + if (boundary_conditions==1) + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c); + return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + return (double)img.linear_atXYZC(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()), + cimg::mod((float)c,(float)img.spectrum())); + if (boundary_conditions==1) + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c); + return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,0); + } + } + + static double mp_list_median(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + if (!mp.list_median) mp.list_median.assign(mp.listin._width); + if (!mp.list_median[ind]) CImg::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]); + return *mp.list_median[ind]; + } + + static double mp_list_set_ioff(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const long + off = (long)_mp_arg(3), + whds = (long)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), y = (int)_mp_arg(4), + z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), + whds = (long)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const double + ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], + oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c]; + const int + x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const long + off = (long)_mp_arg(3), + whd = img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const long + off = (long)_mp_arg(3), + whd = img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_set_Joff_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), + whd = img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(3), + whd = img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; + const int x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), z = (int)(oz + _mp_arg(5)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; + const int x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), z = (int)(oz + _mp_arg(5)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_spectrum(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._spectrum; + } + + static double mp_list_stats(_cimg_math_parser& mp) { + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + k = (unsigned int)_mp_arg(3); + if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); + if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); + return mp.list_stats(ind,k); + } + + static double mp_list_wh(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height; + } + + static double mp_list_whd(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth; + } + + static double mp_list_whds(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum; + } + + static double mp_list_width(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width; + } + + static double mp_list_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4); + const CImg &img = mp.listin[ind]; + const long + off = (long)_mp_arg(3), + whd = img.width()*img.height()*img.depth(); + const T *ptrs; + if (off<0 || off>=whd) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (!img) { + ptrs = &img[cimg::mod(off,whd)]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + case 1 : // Neumann boundary + if (img) { + ptrs = off<0?img._data:&img.back(); + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + default : // Dirichet boundary + std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + } + ptrs = &img[off]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + } + + static double mp_list_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + const CImg &img = mp.listin[ind]; + const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()),c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); + } + return cimg::type::nan(); + } + + static double mp_list_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4); + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], oz = (int)mp.mem[_cimg_mp_z]; + const CImg &img = mp.listin[ind]; + const long + off = img.offset(ox,oy,oz) + (long)_mp_arg(3), + whd = img.width()*img.height()*img.depth(); + const T *ptrs; + if (off<0 || off>=whd) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (!img) { + ptrs = &img[cimg::mod(off,whd)]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + case 1 : // Neumann boundary + if (img) { + ptrs = off<0?img._data:&img.back(); + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + default : // Dirichet boundary + std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + } + ptrs = &img[off]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + } + + static double mp_list_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + const CImg &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()),c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); + } + return cimg::type::nan(); + } + + static double mp_log(_cimg_math_parser& mp) { + return std::log(_mp_arg(2)); + } + + static double mp_log10(_cimg_math_parser& mp) { + return std::log10(_mp_arg(2)); + } + + static double mp_log2(_cimg_math_parser& mp) { + return cimg::log2(_mp_arg(2)); + } + + static double mp_logical_and(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (!val_left) { mp.p_code = p_end - 1; return 0; } + const uptrT mem_right = mp.opcode[3]; + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + static double mp_logical_not(_cimg_math_parser& mp) { + return (double)!_mp_arg(2); + } + + static double mp_logical_or(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (val_left) { mp.p_code = p_end - 1; return 1; } + const uptrT mem_right = mp.opcode[3]; + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + + static double mp_lt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<_mp_arg(3)); + } + + static double mp_lte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<=_mp_arg(3)); + } + + static double mp_matrix_det(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode(3); + return CImg(ptrs,k,k,1,1,true).det(); + } + + static double mp_matrix_diag(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode(3); + CImg(ptrd,k,k,1,1,true) = CImg(ptrs,1,k,1,1,true).get_diagonal(); + return cimg::type::nan(); + } + + static double mp_matrix_eig(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode(3); + CImg val, vec; + CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); + CImg(ptrd,k,1,1,1,true) = val.unroll('x'); + CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); + return cimg::type::nan(); + } + + static double mp_matrix_eye(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int k = (unsigned int)mp.opcode(2); + CImg(ptrd,k,k,1,1,true).identity_matrix(); + return cimg::type::nan(); + } + + static double mp_matrix_inv(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode(3); + CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(); + return cimg::type::nan(); + } + + static double mp_matrix_mul(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode(4), + l = (unsigned int)mp.opcode(5), + m = (unsigned int)mp.opcode(6); + CImg(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); + return cimg::type::nan(); + } + + static double mp_matrix_rot(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5); + CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); + return cimg::type::nan(); + } + + static double mp_matrix_solve(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode(4), + l = (unsigned int)mp.opcode(5), + m = (unsigned int)mp.opcode(6); + CImg(ptrd,m,l,1,1,true) = CImg(ptr2,m,k,1,1,true).get_solve(CImg(ptr1,l,k,1,1,true)); + return cimg::type::nan(); + } + + static double mp_matrix_trace(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode(3); + return CImg(ptrs,k,k,1,1,true).trace(); + } + + static double mp_matrix_trans(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode(3), + l = (unsigned int)mp.opcode(4); + CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_transpose(); + return cimg::type::nan(); + } + static double mp_max(_cimg_math_parser& mp) { - double val = mp.mem[mp.opcode(2)]; - for (unsigned int i = 3; i values(mp.opcode._height - 2); - double *p = values.data(); - for (unsigned int i = 2; i vals(mp.opcode._height - 2); + double *p = vals.data(); + for (unsigned int i = 2; i values(mp.opcode._height - 3); - double *p = values.data(); - for (unsigned int i = 3; ires) res = val; + } + return res; + } + + static double mp_normp(_cimg_math_parser& mp) { + const double p = (double)mp.opcode[2]; + double res = 0; for (unsigned int i = 3; i0?res:0.0; } - static double mp_isnan(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return cimg::type::is_nan(val); + + static double mp_pow(_cimg_math_parser& mp) { + const double v = _mp_arg(2), p = _mp_arg(3); + return std::pow(v,p); } - static double mp_isinf(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return cimg::type::is_inf(val); + + static double mp_pow3(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val; } - static double mp_isint(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return (double)(cimg::mod(val,1.0)==0); + + static double mp_pow4(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val*val; } - static double mp_isbool(_cimg_math_parser& mp) { - const double val = mp.mem[mp.opcode(2)]; - return (val==0.0 || val==1.0); + + static double mp_print(_cimg_math_parser& mp) { + CImg expr(mp.opcode._height - 2); + const uptrT *ptrs = mp.opcode._data + 2; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(expr); + const double val = _mp_arg(1); + std::fprintf(cimg::output(),"\n[_cimg_math_parser] %s = %g",expr._data,val); + std::fflush(cimg::output()); + return val; } + + static double mp_copy(_cimg_math_parser& mp) { + return _mp_arg(2); + } + static double mp_rol(_cimg_math_parser& mp) { - return cimg::rol(mp.mem[mp.opcode(2)],(unsigned int)mp.mem[mp.opcode(3)]); + return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3)); } + static double mp_ror(_cimg_math_parser& mp) { - return cimg::ror(mp.mem[mp.opcode(2)],(unsigned int)mp.mem[mp.opcode(3)]); + return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3)); } - static double mp_lsl(_cimg_math_parser& mp) { - return (long)mp.mem[mp.opcode(2)]<<(unsigned int)mp.mem[mp.opcode(3)]; + + static double mp_round(_cimg_math_parser& mp) { + return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); } - static double mp_lsr(_cimg_math_parser& mp) { - return (long)mp.mem[mp.opcode(2)]>>(unsigned int)mp.mem[mp.opcode(3)]; + + static double mp_self_add(_cimg_math_parser& mp) { + return _mp_arg(1)+=_mp_arg(2); } - static double mp_sinc(_cimg_math_parser& mp) { - return cimg::sinc(mp.mem[mp.opcode(2)]); + + static double mp_self_bitwise_and(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((unsigned long)val & (unsigned long)_mp_arg(2)); } - static double mp_im(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[0]:0; + + static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((long)val<<(unsigned int)_mp_arg(2)); } - static double mp_iM(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[1]:0; + + static double mp_self_bitwise_or(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((unsigned long)val | (unsigned long)_mp_arg(2)); } - static double mp_ia(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[2]:0; + + static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((long)val>>(unsigned int)_mp_arg(2)); } - static double mp_iv(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[3]:0; + + static double mp_self_decrement(_cimg_math_parser& mp) { + return --_mp_arg(1); } - static double mp_is(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[12]:0; + + static double mp_self_increment(_cimg_math_parser& mp) { + return ++_mp_arg(1); } - static double mp_ip(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[13]:0; + + static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2]; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode[2] = mp.opcode[4]; // Scalar argument. + l_opcode.swap(mp.opcode); + uptrT &target = mp.opcode[1]; + while (siz-->0) { target = ptrd++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); } - static double mp_ic(_cimg_math_parser& mp) { - return mp.is_median_value?mp.median_value:0; + + static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + uptrT &target = mp.opcode[1], &argument = mp.opcode[2]; + while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); } - static double mp_xm(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[4]:0; + + static double mp_self_mul(_cimg_math_parser& mp) { + return _mp_arg(1)*=_mp_arg(2); } - static double mp_ym(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[5]:0; + + static double mp_self_div(_cimg_math_parser& mp) { + return _mp_arg(1)/=_mp_arg(2); } - static double mp_zm(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[6]:0; + + static double mp_self_modulo(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = cimg::mod(val,_mp_arg(2)); } - static double mp_cm(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[7]:0; + + static double mp_self_pow(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = std::pow(val,_mp_arg(2)); } - static double mp_xM(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[8]:0; + + static double mp_self_sub(_cimg_math_parser& mp) { + return _mp_arg(1)-=_mp_arg(2); } - static double mp_yM(_cimg_math_parser& mp) { - return mp.reference_stats?mp.reference_stats[9]:0; + + static double mp_set_ioff(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const long + off = (long)_mp_arg(2), + whds = (long)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + x = (int)_mp_arg(2), y = (int)_mp_arg(3), + z = (int)_mp_arg(4), c = (int)_mp_arg(5); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), + whds = (long)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off=nb_args) return 0; - return mp.mem[mp.opcode(ind + 2)]; + + static double mp_set_jxyzc(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const double + ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], + oz = mp.mem[_cimg_mp_z], oc = mp.mem[_cimg_mp_c]; + const int + x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const long + off = (long)_mp_arg(2), + whd = img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off=mp.reference.size()) return 0; - return (double)mp.reference[off]; + + static double mp_set_Ioff_v(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const long + off = (long)_mp_arg(2), + whd = img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); } - static double mp_joff(_cimg_math_parser& mp) { - const int x = (int)mp.mem[16], y = (int)mp.mem[17], z = (int)mp.mem[18], c = (int)mp.mem[19]; - const unsigned long off = mp.reference.offset(x,y,z,c) + (unsigned long)(mp.mem[mp.opcode(2)]); - if (off>=mp.reference.size()) return 0; - return (double)mp.reference[off]; + + static double mp_set_Ixyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int x = (int)_mp_arg(2), y = (int)_mp_arg(3), z = (int)_mp_arg(4); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const int x = (int)_mp_arg(2), y = (int)_mp_arg(3), z = (int)_mp_arg(4); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } - for (p_code = code._data; p_code &op = *p_code; - // Allows to avoid parameter passing to evaluation functions. - opcode._data = op._data; opcode._height = op._height; - const unsigned int target = (unsigned int)opcode[1]; - mem[target] = _cimg_mp_defunc(*this); + static double mp_set_Joff_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), + whd = img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], + oz = (int)mp.mem[_cimg_mp_z], oc = (int)mp.mem[_cimg_mp_c]; + const long + off = img.offset(ox,oy,oz,oc) + (long)_mp_arg(2), + whd = img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_set_Jxyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; + const int x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), z = (int)(oz + _mp_arg(4)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z]; + const int x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), z = (int)(oz + _mp_arg(4)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_sign(_cimg_math_parser& mp) { + return cimg::sign(_mp_arg(2)); + } + + static double mp_sin(_cimg_math_parser& mp) { + return std::sin(_mp_arg(2)); + } + + static double mp_sinc(_cimg_math_parser& mp) { + return cimg::sinc(_mp_arg(2)); + } + + static double mp_sinh(_cimg_math_parser& mp) { + return std::sinh(_mp_arg(2)); + } + + static double mp_sqr(_cimg_math_parser& mp) { + return cimg::sqr(_mp_arg(2)); } - }; + + static double mp_sqrt(_cimg_math_parser& mp) { + return std::sqrt(_mp_arg(2)); + } + + static double mp_sub(_cimg_math_parser& mp) { + return _mp_arg(2) - _mp_arg(3); + } + + static double mp_tan(_cimg_math_parser& mp) { + return std::tan(_mp_arg(2)); + } + + static double mp_tanh(_cimg_math_parser& mp) { + return std::tanh(_mp_arg(2)); + } + + static double mp_u(_cimg_math_parser& mp) { + return cimg::rand(_mp_arg(2),_mp_arg(3)); + } + + static double mp_vector_copy(_cimg_math_parser& mp) { + std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]); + return cimg::type::nan(); + } + + static double mp_vector_crop(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int p1 = mp.opcode[3], p2 = mp.opcode[4]; + std::memcpy(ptrd,ptrs + p1,p2*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_vector_init(_cimg_math_parser& mp) { + unsigned int + ptrs = 3U, + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2]; + switch (mp.opcode._height) { + case 3 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given + case 4 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break; + default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode._height) ptrs = 3U; } + } + return cimg::type::nan(); + } + + static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(4); + l_opcode[2] = mp.opcode[4]; // Scalar argument1 + l_opcode.swap(mp.opcode); + uptrT &argument2 = mp.opcode[3]; + while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode.swap(mp.opcode); + uptrT &argument = mp.opcode[2]; + while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode[3] = mp.opcode[5]; // Scalar argument2 + l_opcode.swap(mp.opcode); + uptrT &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); + uptrT &argument1 = mp.opcode[2]; + while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs1 = (unsigned int)mp.opcode[4] + 1, + ptrs2 = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + uptrT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3]; + while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_off(_cimg_math_parser& mp) { + const unsigned int + ptr = mp.opcode[2] + 1, + siz = (int)mp.opcode[3]; + const int off = (int)_mp_arg(4); + return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type::nan(); + } + + static double mp_vector_set_off(_cimg_math_parser& mp) { + const unsigned int + ptr = mp.opcode[2] + 1, + siz = mp.opcode[3]; + const int off = (int)_mp_arg(4); + if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(5); + return _mp_arg(5); + } + + static double mp_vector_sort(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int siz = mp.opcode[3]; + const bool is_increasing = (bool)_mp_arg(4); + CImg(ptrd,1,siz,1,1,true) = CImg(ptrs,1,siz,1,1,true).get_sort(is_increasing); + return cimg::type::nan(); + } + + static double mp_vector_print(_cimg_math_parser& mp) { + CImg expr(mp.opcode._height - 3); + const uptrT *ptrs = mp.opcode._data + 3; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(expr); + unsigned int + ptr = mp.opcode[1] + 1, + siz = mp.opcode[2]; + std::fprintf(cimg::output(),"\n[_cimg_math_parser] %s = [",expr._data); + while (siz-->0) std::fprintf(cimg::output(),"%g%s",mp.mem[ptr++],siz?",":""); + std::fputc(']',cimg::output()); + std::fflush(cimg::output()); + return cimg::type::nan(); + } + + static double mp_whiledo(_cimg_math_parser& mp) { // Used also by 'for()' + const uptrT + mem_proc = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_cond = ++mp.p_code, + *const p_proc = p_cond + mp.opcode[3], + *const p_end = p_proc + mp.opcode[4]; + const unsigned int vsiz = mp.opcode[5]; + bool is_first_iter = true, is_cond = false; + do { + for (mp.p_code = p_cond; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond) { // Evaluate loop iteration + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; mp.opcode._height = op._height; + const uptrT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + is_first_iter = false; + } + } while (is_cond); + mp.p_code = p_end - 1; + if (vsiz && is_first_iter) std::memset(&mp.mem[mem_proc] + 1,0,vsiz*sizeof(double)); + return is_first_iter?0:mp.mem[mem_proc]; + } + + static double mp_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3); + const CImg &img = mp.imgin; + const long + off = (long)_mp_arg(2), + whd = img.width()*img.height()*img.depth(); + const T *ptrs; + if (off<0 || off>=whd) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (!img) { + ptrs = &img[cimg::mod(off,whd)]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + case 1 : // Neumann boundary + if (img) { + ptrs = off<0?img._data:&img.back(); + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + default : // Dirichet boundary + std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + } + ptrs = &img[off]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + } + + static double mp_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6); + const CImg &img = mp.imgin; + const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()),c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); + } + return cimg::type::nan(); + } + + static double mp_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3); + const CImg &img = mp.imgin; + const int ox = (int)mp.mem[_cimg_mp_x], oy = (int)mp.mem[_cimg_mp_y], oz = (int)mp.mem[_cimg_mp_z]; + const long + off = img.offset(ox,oy,oz) + (long)_mp_arg(2), + whd = img.width()*img.height()*img.depth(); + const T *ptrs; + if (off<0 || off>=whd) + switch (boundary_conditions) { + case 2 : // Periodic boundary + if (!img) { + ptrs = &img[cimg::mod(off,whd)]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + case 1 : // Neumann boundary + if (img) { + ptrs = off<0?img._data:&img.back(); + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + } else std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + default : // Dirichet boundary + std::memset(ptrd,0,img._spectrum*sizeof(double)); + return cimg::type::nan(); + } + ptrs = &img[off]; + cimg_forC(img,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + } + + static double mp_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6); + const CImg &img = mp.imgin; + const double + ox = mp.mem[_cimg_mp_x], oy = mp.mem[_cimg_mp_y], oz = mp.mem[_cimg_mp_z], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4); + if (interpolation==0) { // Nearest neighbor interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ(cimg::mod((int)x,img.width()), + cimg::mod((int)y,img.height()), + cimg::mod((int)z,img.depth()), + c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.atXYZ((int)x,(int)y,(int)z,c,0); + } else { // Linear interpolation + if (boundary_conditions==2) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ(cimg::mod((float)x,(float)img.width()), + cimg::mod((float)y,(float)img.height()), + cimg::mod((float)z,(float)img.depth()),c); + else if (boundary_conditions==1) + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c); + else + cimg_forC(img,c) + *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,0); + } + return cimg::type::nan(); + } + +#undef _mp_arg + + }; // struct _cimg_math_parser {} //! Compute the square value of each pixel value. /** @@ -14925,7 +18920,7 @@ namespace cimg_library_suffixed { CImg& sqr() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if(size()>=524288) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); }; return *this; @@ -14952,7 +18947,7 @@ namespace cimg_library_suffixed { CImg& sqrt() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd); return *this; @@ -14973,7 +18968,7 @@ namespace cimg_library_suffixed { CImg& exp() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=4096) +#pragma omp parallel for cimg_openmp_if(size()>=4096) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd); return *this; @@ -14995,7 +18990,7 @@ namespace cimg_library_suffixed { CImg& log() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=262144) +#pragma omp parallel for cimg_openmp_if(size()>=262144) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd); return *this; @@ -15017,7 +19012,7 @@ namespace cimg_library_suffixed { CImg& log2() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=4096) +#pragma omp parallel for cimg_openmp_if(size()>=4096) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd); return *this; @@ -15039,7 +19034,7 @@ namespace cimg_library_suffixed { CImg& log10() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=4096) +#pragma omp parallel for cimg_openmp_if(size()>=4096) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd); return *this; @@ -15060,7 +19055,7 @@ namespace cimg_library_suffixed { CImg& abs() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=524288) +#pragma omp parallel for cimg_openmp_if(size()>=524288) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::abs(*ptrd); return *this; @@ -15086,7 +19081,7 @@ namespace cimg_library_suffixed { CImg& sign() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::sign(*ptrd); return *this; @@ -15108,7 +19103,7 @@ namespace cimg_library_suffixed { CImg& cos() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd); return *this; @@ -15130,7 +19125,7 @@ namespace cimg_library_suffixed { CImg& sin() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd); return *this; @@ -15153,7 +19148,7 @@ namespace cimg_library_suffixed { CImg& sinc() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if(size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd); return *this; @@ -15175,7 +19170,7 @@ namespace cimg_library_suffixed { CImg& tan() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if(size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd); return *this; @@ -15197,7 +19192,7 @@ namespace cimg_library_suffixed { CImg& cosh() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if(size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd); return *this; @@ -15219,7 +19214,7 @@ namespace cimg_library_suffixed { CImg& sinh() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if(size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd); return *this; @@ -15241,7 +19236,7 @@ namespace cimg_library_suffixed { CImg& tanh() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=2048) +#pragma omp parallel for cimg_openmp_if(size()>=2048) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd); return *this; @@ -15263,7 +19258,7 @@ namespace cimg_library_suffixed { CImg& acos() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd); return *this; @@ -15285,7 +19280,7 @@ namespace cimg_library_suffixed { CImg& asin() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd); return *this; @@ -15307,7 +19302,7 @@ namespace cimg_library_suffixed { CImg& atan() { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd); return *this; @@ -15436,35 +19431,35 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; if (p==-4) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); } return *this; } if (p==-3) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); } return *this; } if (p==-2) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); } return *this; } if (p==-1) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); } return *this; } if (p==-0.5) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); } return *this; @@ -15475,20 +19470,20 @@ namespace cimg_library_suffixed { if (p==2) return sqr(); if (p==3) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=262144) +#pragma omp parallel for cimg_openmp_if(size()>=262144) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; } if (p==4) { #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=131072) +#pragma omp parallel for cimg_openmp_if(size()>=131072) #endif cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; } #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=1024) +#pragma omp parallel for cimg_openmp_if(size()>=1024) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p); return *this; @@ -15504,44 +19499,7 @@ namespace cimg_library_suffixed { Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. **/ CImg& pow(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"pow"); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)std::pow((double)*ptrd,lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode(omode); - values.load(expression); - } - pow(values); - } - cimg::exception_mode(omode); - return *this; + return pow((+*this)._fill(expression,true,true,0,0,"pow",this)); } //! Raise each pixel value to a power, specified from an expression \newinstance. @@ -15580,7 +19538,7 @@ namespace cimg_library_suffixed { CImg& rol(const unsigned int n=1) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n); return *this; @@ -15596,45 +19554,7 @@ namespace cimg_library_suffixed { Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. **/ CImg& rol(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"rol"); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') - cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode(omode); - values.load(expression); - } - rol(values); - } - cimg::exception_mode(omode); - return *this; + return rol((+*this)._fill(expression,true,true,0,0,"rol",this)); } //! Compute the bitwise left rotation of each pixel value \newinstance. @@ -15673,7 +19593,7 @@ namespace cimg_library_suffixed { CImg& ror(const unsigned int n=1) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n); return *this; @@ -15689,45 +19609,7 @@ namespace cimg_library_suffixed { Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. **/ CImg& ror(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"ror"); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') - cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode(omode); - values.load(expression); - } - ror(values); - } - cimg::exception_mode(omode); - return *this; + return ror((+*this)._fill(expression,true,true,0,0,"ror",this)); } //! Compute the bitwise right rotation of each pixel value \newinstance. @@ -15768,7 +19650,7 @@ namespace cimg_library_suffixed { CImg& min(const T& val) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if(size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val); return *this; @@ -15812,43 +19694,7 @@ namespace cimg_library_suffixed { \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& min(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"min"); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::min(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode(omode); - values.load(expression); - } - min(values); - } - cimg::exception_mode(omode); - return *this; + return min((+*this)._fill(expression,true,true,0,0,"min",this)); } //! Pointwise min operator between an image and an expression \newinstance. @@ -15865,7 +19711,7 @@ namespace cimg_library_suffixed { CImg& max(const T& val) { if (is_empty()) return *this; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if(size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val); return *this; @@ -15909,43 +19755,7 @@ namespace cimg_library_suffixed { \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& max(const char *const expression) { - if (is_empty()) return *this; - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"max"); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); --ptrd; } - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } - else { -#ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) -#pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) { *ptrd = (T)cimg::max(*ptrd,(T)lmp(x,y,z,c)); ++ptrd; } - } - } - else -#endif - cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp(x,y,z,c)); ++ptrd; } - } - } catch (CImgException&) { - CImg values(_width,_height,_depth,_spectrum); - try { - values.fill(expression,true); - } catch (CImgException&) { - cimg::exception_mode(omode); - values.load(expression); - } - max(values); - } - cimg::exception_mode(omode); - return *this; + return max((+*this)._fill(expression,true,true,0,0,"max",this)); } //! Pointwise max operator between an image and an expression \newinstance. @@ -16094,7 +19904,7 @@ namespace cimg_library_suffixed { cimg_instance); CImg arr(*this); unsigned int l = 0, ir = size() - 1; - for (;;) { + for ( ; ; ) { if (ir<=l + 1) { if (ir==l + 1 && arr[ir]arr[l + 1]) cimg::swap(arr[l],arr[l + 1]); unsigned int i = l + 1, j = ir; const T pivot = arr[l + 1]; - for (;;) { + for ( ; ; ) { do ++i; while (arr[i]pivot); if (j=262144 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height>=262144 && _spectrum>=2) #endif cimg_forC(*this,c) { CImg_3x3(I,T); @@ -16268,7 +20078,7 @@ namespace cimg_library_suffixed { } else { const double cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=262144 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=262144 && _spectrum>=2) #endif cimg_forC(*this,c) { CImg_3x3x3(I,T); @@ -16348,9 +20158,25 @@ namespace cimg_library_suffixed { \param y Value of the pre-defined variable \c y. \param z Value of the pre-defined variable \c z. \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param list_outputs A pointer to a list of output images attached to the specified math formula. **/ double eval(const char *const expression, - const double x=0, const double y=0, const double z=0, const double c=0) const { + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,x,y,z,c,list_inputs,list_outputs); + } + + double _eval(CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { if (!expression) return 0; if (!expression[1]) switch (*expression) { // Single-char optimization. case 'w' : return (double)_width; @@ -16359,7 +20185,54 @@ namespace cimg_library_suffixed { case 's' : return (double)_spectrum; case 'r' : return (double)_is_shared; } - return _cimg_math_parser(*this,expression,"eval")(x,y,z,c); + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0),"eval", + *this,img_output,list_inputs,list_outputs); + return mp(x,y,z,c); + } + + //! Evaluate math formula. + /** + \param[out] output Contains values of output vector returned by the evaluated expression + (or is empty if the returned type is scalar). + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + template + void eval(CImg &output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + template + void eval(CImg& output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); + } + + template + void _eval(CImg& output, CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression) { output.assign(1); *output = 0; } + if (!expression[1]) switch (*expression) { // Single-char optimization. + case 'w' : output.assign(1); *output = (t)_width; + case 'h' : output.assign(1); *output = (t)_height; + case 'd' : output.assign(1); *output = (t)_depth; + case 's' : output.assign(1); *output = (t)_spectrum; + case 'r' : output.assign(1); *output = (t)_is_shared; + } + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0),"eval", + *this,img_output,list_inputs,list_outputs); + output.assign(1,cimg::max(1U,mp.result_dim)); + mp(x,y,z,c,output._data); } //! Evaluate math formula on a set of variables. @@ -16368,10 +20241,24 @@ namespace cimg_library_suffixed { \param xyzc Set of values (x,y,z,c) used for the evaluation. **/ template - CImg eval(const char *const expression, const CImg& xyzc) const { + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,xyzc,list_inputs,list_outputs); + } + + //! Evaluate math formula on a set of variables \const. + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,xyzc,list_inputs,list_outputs); + } + + template + CImg _eval(CImg *const output, const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { CImg res(1,xyzc.size()/4); if (!expression) return res.fill(0); - _cimg_math_parser mp(*this,expression,"eval"); + _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs); #ifdef cimg_use_openmp #pragma omp parallel if (res._height>=512 && std::strlen(expression)>=6) { @@ -18496,36 +22383,95 @@ namespace cimg_library_suffixed { \param expression C-string describing a math formula, or a list of values. \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling. \param allow_formula tells if a formula is allowed or only a list of values. + \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression. + \param list_outputs In case of a mathematical expression, attach a list of images to the specified expression. **/ - CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true) { + CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _fill(expression,repeat_values,allow_formula,list_inputs,list_outputs,"fill",0); + } + + CImg& _fill(const char *const expression, const bool repeat_values, const bool allow_formula, + const CImgList *const list_inputs, CImgList *const list_outputs, + const char *const calling_function, const CImg *provide_base) { if (is_empty() || !expression || !*expression) return *this; const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); CImg is_error; - if (allow_formula) try { // Try to fill values according to a formula. - const CImg _base = cimg::_is_self_expr(expression)?+*this:CImg(), &base = _base?_base:*this; - _cimg_math_parser mp(base,expression + (*expression=='>' || *expression=='<'?1:0),"fill"); - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') cimg_rofXYZC(*this,x,y,z,c) *(ptrd--) = (T)mp(x,y,z,c); - else if (*expression=='>') cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); - else { + if (allow_formula) try { // Try to fill values according to a formula + bool is_parallelizable = true; + const CImg + _base = _cimg_math_parser::needs_input_copy(expression,is_parallelizable)? + (provide_base?*provide_base:+*this):CImg(), + &base = provide_base?*provide_base:_base?_base:*this; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*'?1:0), + calling_function,base,this,list_inputs,list_outputs); + bool do_in_parallel = false; +#ifdef cimg_use_openmp + cimg_openmp_if(*expression=='*' || + (is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2 && + std::strlen(expression)>=6)) + do_in_parallel = true; +#endif + if (mp.result_dim) { // Vector-valued expression + const unsigned int N = cimg::min(mp.result_dim,_spectrum); + const unsigned long whd = _width*_height*_depth; + T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; + if (*expression=='<') { + CImg res(1,mp.result_dim); + cimg_rofXYZ(*this,x,y,z) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } else if (*expression=='>' || !do_in_parallel) { + CImg res(1,mp.result_dim); + cimg_forXYZ(*this,x,y,z) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } else { #ifdef cimg_use_openmp - if (_width>=512 && _height*_depth*_spectrum>=2 && std::strlen(expression)>=6) #pragma omp parallel - { - _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; -#pragma omp for collapse(3) - cimg_forYZC(*this,y,z,c) { - T *ptrd = data(0,y,z,c); - cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; +#pragma omp for collapse(2) + cimg_forYZ(*this,y,z) { + CImg res(1,lmp.result_dim); + T *ptrd = data(0,y,z,0); + cimg_forX(*this,x) { + lmp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } } +#endif } - else + + } else { // Scalar-valued expression + T *ptrd = *expression=='<'?end() - 1:_data; + if (*expression=='<') + cimg_rofXYZC(*this,x,y,z,c) *(ptrd--) = (T)mp(x,y,z,c); + else if (*expression=='>' || !do_in_parallel) + cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); + else { +#ifdef cimg_use_openmp +#pragma omp parallel + { + _cimg_math_parser _mp = omp_get_thread_num()?mp:_cimg_math_parser(), &lmp = omp_get_thread_num()?_mp:mp; +#pragma omp for collapse(3) + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); + } + } #endif - cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp(x,y,z,c); - } - } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } + } + } + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } // If failed, try to recognize a list of values. if (!allow_formula || is_error) { @@ -18547,8 +22493,8 @@ namespace cimg_library_suffixed { if (nb get_fill(const char *const values, const bool repeat_values, const bool allow_formula=true) const { - return (+*this).fill(values,repeat_values,allow_formula); + CImg get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return (+*this).fill(expression,repeat_values,allow_formula,list_inputs,list_outputs); } //! Fill sequentially pixel values according to the values found in another image. @@ -18811,9 +22758,9 @@ namespace cimg_library_suffixed { //! Fill image with random values in specified range. /** - \param val_min Minimal random value. - \param val_max Maximal random value. - \note Random samples are following a uniform distribution. + \param val_min Minimal authorized random value. + \param val_max Maximal authorized random value. + \note Random variables are uniformely distributed in [val_min,val_max]. **/ CImg& rand(const T& val_min, const T& val_max) { const float delta = (float)val_max - (float)val_min + (cimg::type::is_float()?0:1); @@ -18838,7 +22785,7 @@ namespace cimg_library_suffixed { CImg& round(const double y=1, const int rounding_type=0) { if (y>0) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=8192) +#pragma omp parallel for cimg_openmp_if(size()>=8192) #endif cimg_rof(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type); return *this; @@ -18885,7 +22832,7 @@ namespace cimg_library_suffixed { } break; case 1 : { // Uniform noise cimg_rof(*this,ptrd,T) { - Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::crand()); + Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::rand(-1,1)); if (val>vmax) val = vmax; if (val::is_float()?1:cimg::type::max()); } - cimg_rof(*this,ptrd,T) if (cimg::rand()*100=65536) +#pragma omp parallel for cimg_openmp_if(size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a); return *this; @@ -19112,7 +23059,7 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; const T a = min_value=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) *ptrd = (*ptrdb)?b:*ptrd); return *this; @@ -19145,14 +23092,14 @@ namespace cimg_library_suffixed { if (range>0) { if (keep_range) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); *ptrd = (T)(m + cimg::min(val,nb_levels - 1)*range/nb_levels); } else #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range); @@ -19184,7 +23131,7 @@ namespace cimg_library_suffixed { if (strict_threshold) { if (soft_threshold) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T v = *ptrd; @@ -19192,13 +23139,13 @@ namespace cimg_library_suffixed { } else #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if(size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0; } else { if (soft_threshold) #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=32768) +#pragma omp parallel for cimg_openmp_if(size()>=32768) #endif cimg_rof(*this,ptrd,T) { const T v = *ptrd; @@ -19206,7 +23153,7 @@ namespace cimg_library_suffixed { } else #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=65536) +#pragma omp parallel for cimg_openmp_if(size()>=65536) #endif cimg_rof(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0; } @@ -19290,7 +23237,7 @@ namespace cimg_library_suffixed { cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } if (!cumul) cumul = 1; #ifdef cimg_use_openmp -#pragma omp parallel for if (size()>=1048576) +#pragma omp parallel for cimg_openmp_if(size()>=1048576) #endif cimg_rof(*this,ptrd,T) { const int pos = (int)((*ptrd-vmin)*(nb_levels - 1.)/(vmax-vmin)); @@ -20903,7 +24850,7 @@ namespace cimg_library_suffixed { cc = (int)(centering_c*((int)sc - spectrum())); switch (boundary_conditions) { - case 2 : { // Periodic borders. + case 2 : { // Periodic boundary. res.assign(sx,sy,sz,sc); const int x0 = ((int)xc%width()) - width(), @@ -20919,7 +24866,7 @@ namespace cimg_library_suffixed { for (int x = x0; x<(int)sx; x+=width()) res.draw_image(x,y,z,c,*this); } break; - case 1 : { // Neumann borders. + case 1 : { // Neumann boundary. res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this); CImg sprite; if (xc>0) { // X-backward @@ -20957,7 +24904,7 @@ namespace cimg_library_suffixed { for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite); } } break; - default : // Dirichlet borders. + default : // Dirichlet boundary. res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this); } break; @@ -21028,17 +24975,17 @@ namespace cimg_library_suffixed { cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); } ++y; unsigned long dy = *(poff_y++); - for (;!dy && y get_resize_halfXY() const { if (is_empty()) return *this; - const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, - 0.1231940459f, 0.1935127547f, 0.1231940459f, - 0.07842776544f, 0.1231940459f, 0.07842776544f }; - T I[9] = { 0 }; - CImg res(_width/2,_height/2,_depth,_spectrum); + static const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f }; + CImg I(9), res(_width/2,_height/2,_depth,_spectrum); T *ptrd = res._data; cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) if (x%2 && y%2) *(ptrd++) = (T) @@ -22895,7 +26841,7 @@ namespace cimg_library_suffixed { cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0); } } - } else { // Absolute warp. + } else { // Backward-absolute warp. if (interpolation==2) { // Cubic interpolation. if (boundary_conditions==2) // Periodic boundaries. #ifdef cimg_use_openmp @@ -23073,7 +27019,7 @@ namespace cimg_library_suffixed { cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0); } } - } else { // Absolute warp. + } else { // Backward-absolute warp. if (interpolation==2) { // Cubic interpolation. if (boundary_conditions==2) // Periodic boundaries. #ifdef cimg_use_openmp @@ -23146,7 +27092,7 @@ namespace cimg_library_suffixed { } } - } else if (warp._spectrum==3) { // 3d warping. + } else { // 3d warping. if (mode>=3) { // Forward-relative warp. res.fill(0); if (interpolation>=1) // Linear interpolation. @@ -23275,7 +27221,7 @@ namespace cimg_library_suffixed { cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0); } } - } else { // Absolute warp. + } else { // Backward-absolute warp. if (interpolation==2) { // Cubic interpolation. if (boundary_conditions==2) // Periodic boundaries. #ifdef cimg_use_openmp @@ -23989,7 +27935,7 @@ namespace cimg_library_suffixed { _cimg_math_parser *mp; ~_functor4d_streamline_expr() { delete mp; } _functor4d_streamline_expr(const char *const expr):mp(0) { - mp = new _cimg_math_parser(CImg::empty(),expr,"streamline"); + mp = new _cimg_math_parser(expr,"streamline",CImg::const_empty(),0); } float operator()(const float x, const float y, const float z, const unsigned int c) const { return (float)(*mp)(x,y,z,c); @@ -24214,7 +28160,7 @@ namespace cimg_library_suffixed { res.assign(_width/dp + (_width%dp?1:0),1,1); const unsigned int pe = _width - dp; #ifdef cimg_use_openmp -#pragma omp parallel for if (res._width>=128 && _height*_depth*_spectrum>=128) +#pragma omp parallel for cimg_openmp_if(res._width>=128 && _height*_depth*_spectrum>=128) #endif for (unsigned int p = 0; p=128 && _width*_depth*_spectrum>=128) +#pragma omp parallel for cimg_openmp_if(res._width>=128 && _width*_depth*_spectrum>=128) #endif for (unsigned int p = 0; p=128 && _width*_height*_spectrum>=128) +#pragma omp parallel for cimg_openmp_if(res._width>=128 && _width*_height*_spectrum>=128) #endif for (unsigned int p = 0; p=128 && _width*_height*_depth>=128) +#pragma omp parallel for cimg_openmp_if(res._width>=128 && _width*_height*_depth>=128) #endif for (unsigned int p = 0; p I; switch (mask._depth) { case 3 : { - T I[27] = { 0 }; + I.assign(27); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24599,8 +28547,9 @@ namespace cimg_library_suffixed { } } break; case 2 : { - T I[8] = { 0 }; + I.assign(8); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24626,8 +28575,9 @@ namespace cimg_library_suffixed { case 1 : switch (mask._width) { case 6 : { - T I[36] = { 0 }; + I.assign(36); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24665,8 +28615,9 @@ namespace cimg_library_suffixed { } } break; case 5 : { - T I[25] = { 0 }; + I.assign(25); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24696,8 +28647,9 @@ namespace cimg_library_suffixed { } } break; case 4 : { - T I[16] = { 0 }; + I.assign(16); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24721,8 +28673,9 @@ namespace cimg_library_suffixed { } } break; case 3 : { - T I[9] = { 0 }; + I.assign(9); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24742,8 +28695,9 @@ namespace cimg_library_suffixed { } } break; case 2 : { - T I[4] = { 0 }; + I.assign(4); cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { @@ -24762,6 +28716,7 @@ namespace cimg_library_suffixed { case 1 : if (is_normalized) res.fill(1); else cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); res.get_shared_channel(c).assign(_img)*=_mask[0]; @@ -24769,15 +28724,16 @@ namespace cimg_library_suffixed { break; } } - } else { // Generic version for other masks and borders conditions. + } else { // Generic version for other masks and boundary conditions. const int mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2, mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; #ifdef cimg_use_openmp -#pragma omp parallel for if (res._spectrum>=2) +#pragma omp parallel for cimg_openmp_if(res._spectrum>=2) #endif cimg_forC(res,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { // Normalized correlation. @@ -25009,9 +28965,10 @@ namespace cimg_library_suffixed { mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { // Normalized erosion. @@ -25298,9 +29255,10 @@ namespace cimg_library_suffixed { mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2), mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { + cimg_test_abort(); const CImg _img = get_shared_channel(c%_spectrum); const CImg _mask = mask.get_shared_channel(c%mask._spectrum); if (is_normalized) { // Normalized dilation. @@ -25614,44 +29572,44 @@ namespace cimg_library_suffixed { // Check labels of the neighbors. bool is_same_label = true; - unsigned int label = 0; + T label = 0; if (x - 1>=0) { if ((*this)(x - 1,y,z)) { - if (!label) label = (unsigned int)(*this)(x - 1,y,z); + if (!label) label = (*this)(x - 1,y,z); else if (label!=(*this)(x - 1,y,z)) is_same_label = false; } else Q._priority_queue_insert(is_queued,sizeQ,priority(x - 1,y,z),x - 1,y,z); } if (x + 1=0) { if ((*this)(x,y - 1,z)) { - if (!label) label = (unsigned int)(*this)(x,y - 1,z); + if (!label) label = (*this)(x,y - 1,z); else if (label!=(*this)(x,y - 1,z)) is_same_label = false; } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y - 1,z),x,y - 1,z); } if (y + 1=0) { if ((*this)(x,y,z - 1)) { - if (!label) label = (unsigned int)(*this)(x,y,z - 1); + if (!label) label = (*this)(x,y,z - 1); else if (label!=(*this)(x,y,z - 1)) is_same_label = false; } else Q._priority_queue_insert(is_queued,sizeQ,priority(x,y,z - 1),x,y,z - 1); } if (z + 1=256 && _height*_depth>=2) firstprivate(val) #endif @@ -26338,8 +30299,9 @@ namespace cimg_library_suffixed { *(pd2++) = (Tfloat)n; } + cimg_test_abort(); #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=256 && _height>=2) firstprivate(val) +#pragma omp parallel for cimg_openmp_if(_width>=256 && _height>=2) firstprivate(val) #endif cimg_forXY(*this,x,y) { val.fill(0); @@ -27017,7 +30979,7 @@ namespace cimg_library_suffixed { const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; if (is_fast_approx) #ifdef cimg_use_openmp -#pragma omp parallel for if (res._width>=32 && res._height>=4) firstprivate(P,Q) +#pragma omp parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q) #endif cimg_forXY(res,x,y) { // 2d fast approximation. P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); @@ -27036,7 +30998,7 @@ namespace cimg_library_suffixed { else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); } else #ifdef cimg_use_openmp -#pragma omp parallel for if (res._width>=32 && res._height>=4) firstprivate(P,Q) +#pragma omp parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q) #endif cimg_forXY(res,x,y) { // 2d exact algorithm. P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); @@ -27130,7 +31092,7 @@ namespace cimg_library_suffixed { else switch (n) { // Without threshold. case 3 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { T I[9] = { 0 }; @@ -27150,7 +31112,7 @@ namespace cimg_library_suffixed { } break; case 5 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { T I[25] = { 0 }; @@ -27209,9 +31171,10 @@ namespace cimg_library_suffixed { } } else { // 1d + CImg I; if (threshold>0) #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=16 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width>=16 && _spectrum>=2) #endif cimg_forXC(*this,x,c) { // With threshold. const int @@ -27228,26 +31191,26 @@ namespace cimg_library_suffixed { else switch (n) { // Without threshold. case 2 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { - T I[4] = { 0 }; + I.assign(4); cimg_for2x2(*this,x,y,0,c,I,T) res(x,c) = (T)(0.5f*(I[0] + I[1])); } } break; case 3 : { #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { - T I[9] = { 0 }; + I.assign(9); cimg_for3x3(*this,x,y,0,c,I,T) res(x,c) = I[3]=16 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width>=16 && _spectrum>=2) #endif cimg_forXC(*this,x,c) { const int @@ -27300,7 +31263,7 @@ namespace cimg_library_suffixed { } } #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=512 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=512 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; @@ -27347,7 +31310,7 @@ namespace cimg_library_suffixed { CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); if (sigma>0) G.blur(sigma); #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=32 && _height>=16) +#pragma omp parallel for cimg_openmp_if(_width>=32 && _height>=16) #endif cimg_forY(G,y) { CImg val, vec; @@ -27362,7 +31325,7 @@ namespace cimg_library_suffixed { } } #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=512 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height>=512 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; @@ -27444,7 +31407,7 @@ namespace cimg_library_suffixed { switch (scheme) { // 3d. case -1 : { // Backward finite differences. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27459,7 +31422,7 @@ namespace cimg_library_suffixed { } break; case 1 : { // Forward finite differences. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27484,7 +31447,7 @@ namespace cimg_library_suffixed { } break; default : { // Central finite differences. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27612,7 +31575,7 @@ namespace cimg_library_suffixed { if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { const unsigned long off = c*_width*_height*_depth; @@ -27673,7 +31636,7 @@ namespace cimg_library_suffixed { else if (axis1=='x' && axis2=='z') { // Ixz valid_axis = true; #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res[l2].data(0,0,0,c); @@ -27695,7 +31658,7 @@ namespace cimg_library_suffixed { else if (axis1=='y' && axis2=='z') { // Iyz valid_axis = true; #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res[l2].data(0,0,0,c); @@ -27706,7 +31669,7 @@ namespace cimg_library_suffixed { else if (axis1=='z' && axis2=='z') { // Izz valid_axis = true; #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res[l2].data(0,0,0,c); @@ -27734,7 +31697,7 @@ namespace cimg_library_suffixed { CImg res(_width,_height,_depth,_spectrum); if (_depth>1) { // 3d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res.data(0,0,0,c); @@ -27743,7 +31706,7 @@ namespace cimg_library_suffixed { } } else if (_height>1) { // 2d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res.data(0,0,0,c); @@ -27752,7 +31715,7 @@ namespace cimg_library_suffixed { } } else { // 1d #ifdef cimg_use_openmp -#pragma omp parallel for if (_width>=1048576 && _height*_depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width>=1048576 && _height*_depth*_spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd = res.data(0,0,0,c); @@ -27765,22 +31728,21 @@ namespace cimg_library_suffixed { //! Compute the structure tensor field of an image. /** - \param scheme Numerical scheme. Can be { 0=central | 1=fwd/bwd1 | 2=fwd/bwd2 } + \param is_fwbw_scheme scheme. Can be { false=centered | true=forward-backward } **/ - CImg& structure_tensors(const unsigned int scheme=2) { - return get_structure_tensors(scheme).move_to(*this); + CImg& structure_tensors(const bool is_fwbw_scheme=false) { + return get_structure_tensors(is_fwbw_scheme).move_to(*this); } //! Compute the structure tensor field of an image \newinstance. - CImg get_structure_tensors(const unsigned int scheme=2) const { + CImg get_structure_tensors(const bool is_fwbw_scheme=false) const { if (is_empty()) return *this; CImg res; if (_depth>1) { // 3d res.assign(_width,_height,_depth,6,0); - switch (scheme) { - case 0 : { // classical central finite differences + if (!is_fwbw_scheme) { // Classical central finite differences #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat @@ -27800,33 +31762,9 @@ namespace cimg_library_suffixed { *(ptrd5++)+=iz*iz; } } - } break; - case 1 : { // Forward/backward finite differences (version 1). -#ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat - *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), - *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); - CImg_3x3x3(I,Tfloat); - cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { - const Tfloat - ixf = Incc - Iccc, ixb = Iccc - Ipcc, - iyf = Icnc - Iccc, iyb = Iccc - Icpc, - izf = Iccn - Iccc, izb = Iccc - Iccp; - *(ptrd0++)+=(ixf*ixf + 2*ixf*ixb + ixb*ixb)/4; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; - *(ptrd3++)+=(iyf*iyf + 2*iyf*iyb + iyb*iyb)/4; - *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; - *(ptrd5++)+=(izf*izf + 2*izf*izb + izb*izb)/4; - } - } - } break; - default : { // Forward/backward finite differences (version 2). + } else { // Forward/backward finite differences. #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height*_depth>=1048576 && _spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2) #endif cimg_forC(*this,c) { Tfloat @@ -27846,14 +31784,12 @@ namespace cimg_library_suffixed { *(ptrd5++)+=(izf*izf + izb*izb)/2; } } - } break; } } else { // 2d res.assign(_width,_height,_depth,3,0); - switch (scheme) { - case 0 : { // classical central finite differences + if (!is_fwbw_scheme) { // Classical central finite differences #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); @@ -27867,27 +31803,9 @@ namespace cimg_library_suffixed { *(ptrd2++)+=iy*iy; } } - } break; - case 1 : { // Forward/backward finite differences (version 1). + } else { // Forward/backward finite differences (version 2). #ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) -#endif - cimg_forC(*this,c) { - Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); - 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 + 2*ixf*ixb + ixb*ixb)/4; - *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; - *(ptrd2++)+=(iyf*iyf + 2*iyf*iyb + iyb*iyb)/4; - } - } - } break; - default : { // Forward/backward finite differences (version 2). -#ifdef cimg_use_openmp -#pragma omp parallel for if (_width*_height>=1048576 && _depth*_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2) #endif cimg_forC(*this,c) { Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); @@ -27901,7 +31819,6 @@ namespace cimg_library_suffixed { *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; } } - } break; } } return res; @@ -27992,14 +31909,15 @@ namespace cimg_library_suffixed { \param nb_scales Number of scales used to estimate the displacement field. \param iteration_max Maximum number of iterations allowed for one scale. \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)). - \param constraints A list of constrained pixels (as a Nx4 or Nx6 image), i.e defining N points - of the estimated flow having a known value. + \param guide Image used as the initial correspondence estimate for the algorithm. + 'guide' may have a last channel with boolean values (0=false | other=true) that + tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). **/ CImg& displacement(const CImg& source, const float smoothness=0.1f, const float precision=5.0f, const unsigned int nb_scales=0, const unsigned int iteration_max=10000, const bool is_backward=false, - const CImg& constraints=CImg::empty()) { - return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,constraints). + const CImg& guide=CImg::const_empty()) { + return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). move_to(*this); } @@ -28008,7 +31926,7 @@ namespace cimg_library_suffixed { const float smoothness=0.1f, const float precision=5.0f, const unsigned int nb_scales=0, const unsigned int iteration_max=10000, const bool is_backward=false, - const CImg& constraints=CImg::empty()) const { + const CImg& guide=CImg::const_empty()) const { if (is_empty() || !source) return +*this; if (!is_sameXYZC(source)) throw CImgArgumentException(_cimg_instance @@ -28022,7 +31940,18 @@ namespace cimg_library_suffixed { "(should be >=0)", cimg_instance, precision); + const bool is_3d = source._depth>1; + const unsigned int constraint = is_3d?3:2; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum0?nb_scales: @@ -28032,14 +31961,7 @@ namespace cimg_library_suffixed { float sm, sM = source.max_min(sm), tm, tM = max_min(tm); const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm); - if (constraints && (constraints.height()!=4 || is_3d) && (constraints.height()!=6 || !is_3d)) - throw CImgArgumentException(_cimg_instance - "displacement(): Invalid specified constraints image (%u,%u,%u,%u,%p) " - " (should be a Nx4 or Nx6 image).", - cimg_instance, - constraints._width,constraints._height,constraints._depth,constraints._spectrum, - constraints._data); - CImg U; + CImg U, V; floatT bound = 0; for (int scale = (int)_nb_scales - 1; scale>=0; --scale) { const float factor = (float)std::pow(1.5,(double)scale); @@ -28051,36 +31973,21 @@ namespace cimg_library_suffixed { const CImg I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta, I2 = (get_resize(I1,2)-=tm)/=tdelta; + if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V); if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3); - else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); - if (constraints) { - if (is_3d) cimg_forX(constraints,k) { - const int - cx = (int)(constraints(k,0)*U.width()/width()), - cy = (int)(constraints(k,1)*U.height()/height()), - cz = (int)(constraints(k,2)*U.depth()/depth()); - if (U.contains(U(cx,cy,cz))) { - U(cx,cy,cz,0) = (float)(constraints(k,3)/factor); - U(cx,cy,cz,1) = (float)(constraints(k,4)/factor); - U(cx,cy,cz,2) = (float)(constraints(k,5)/factor); - } - } - else cimg_forX(constraints,k) { - const int - cx = (int)(constraints(k,0)*U.width()/width()), - cy = (int)(constraints(k,1)*U.height()/height()); - if (U.contains(U(cx,cy))) { - U(cx,cy,0) = (float)(constraints(k,2)/factor); - U(cx,cy,1) = (float)(constraints(k,3)/factor); - } - } + else { + if (guide) + guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U); + else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); } float dt = 2, energy = cimg::type::max(); const CImgList dI = is_backward?I1.get_gradient():I2.get_gradient(); for (unsigned int iteration = 0; iteration=0) // Isotropic regularization. #ifdef cimg_use_openmp @@ -28127,6 +32034,11 @@ namespace cimg_library_suffixed { } _energy+=delta_I*delta_I + smoothness*_energy_regul; } + if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints. + U(x,y,z,0) = V(x,y,z,0)/factor; + U(x,y,z,1) = V(x,y,z,1)/factor; + U(x,y,z,2) = V(x,y,z,2)/factor; + } } else { // Anisotropic regularization. const float nsmoothness = -smoothness; #ifdef cimg_use_openmp @@ -28188,23 +32100,17 @@ namespace cimg_library_suffixed { } _energy+=delta_I*delta_I + nsmoothness*_energy_regul; } + if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints. + U(x,y,z,0) = V(x,y,z,0)/factor; + U(x,y,z,1) = V(x,y,z,1)/factor; + U(x,y,z,2) = V(x,y,z,2)/factor; + } } } - if (constraints) cimg_forX(constraints,k) { - const int - cx = (int)(constraints(k,0)*U.width()/width()), - cy = (int)(constraints(k,1)*U.height()/height()), - cz = (int)(constraints(k,2)*U.depth()/depth()); - if (U.contains(U(cx,cy,cz))) { - U(cx,cy,cz,0) = (float)(constraints(k,3)/factor); - U(cx,cy,cz,1) = (float)(constraints(k,4)/factor); - U(cx,cy,cz,2) = (float)(constraints(k,5)/factor); - } - } } else { // 2d version. if (smoothness>=0) // Isotropic regularization. #ifdef cimg_use_openmp -#pragma omp parallel for if (_height>=8 && _width>=16) reduction(+:_energy) +#pragma omp parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy) #endif cimg_forY(U,y) { const int _p1y = y?y - 1:0, _n1y = y=8 && _width>=16) reduction(+:_energy) +#pragma omp parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy) #endif cimg_forY(U,y) { const int _p1y = y?y - 1:0, _n1y = y + CImg& patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const CImg &guide, + CImg &matching_score) { + return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,guide,matching_score).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \newinstance. + template + CImg get_patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const CImg &guide, + CImg &matching_score) const { + return _get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms, + guide,true,matching_score); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg& patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const CImg &guide) { + return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,guide).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg get_patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const CImg &guide) const { + return _get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms, + guide,false,CImg::empty()); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + CImg& patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth=1, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5) { + return get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + CImg get_patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth=1, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5) const { + return _get_patchmatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms, + CImg::const_empty(), + false,CImg::empty()); + } + + template + CImg _get_patchmatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const CImg &guide, + const bool is_matching_score, + CImg &matching_score) const { + if (is_empty()) return CImg::const_empty(); + if (patch_image._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "patchmatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) " + "have different spectrums.", + cimg_instance, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + if (patch_width>_width || patch_height>_height || patch_depth>_depth) + throw CImgArgumentException(_cimg_instance + "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the instance image.", + cimg_instance,patch_width,patch_height,patch_depth); + if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth) + throw CImgArgumentException(_cimg_instance + "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the patch image image (%u,%u,%u,%u,%p).", + cimg_instance,patch_width,patch_height,patch_depth, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + const unsigned int + _constraint = patch_image._depth>1?3:2, + constraint = guide._spectrum>_constraint?_constraint:0; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint)) + throw CImgArgumentException(_cimg_instance + "patchmatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions " + "considering instance and patch image image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + + CImg map(_width,_height,_depth,patch_image._depth>1?3:2); + CImg score(_width,_height,_depth); + const int + psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1, + psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1, + psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1; + + if (_depth>1 || patch_image._depth>1) { // 3d version. + + // Initialize correspondence map. + if (guide) cimg_forXYZ(*this,x,y,z) { // User-defined initialization. + const int + cx1 = x<=psizew1?x:(x::inf()); + } else cimg_forXYZ(*this,x,y,z) { // Random initialization. + const int + cx1 = x<=psizew1?x:(x::inf()); + } + + // Start iteration loop. + for (unsigned int iter = 0; iter64 && iter0) { // Compare with left neighbor. + const int u = map(x - 1,y,z,0), v = map(x - 1,y,z,1), w = map(x - 1,y,z,2); + if (u>=cx1 - 1 && u=cy1 && v=cz1 && w0) { // Compare with up neighbor. + const int u = map(x,y - 1,z,0), v = map(x,y - 1,z,1), w = map(x,y - 1,z,2); + if (u>=cx1 && u=cy1 - 1 && v=cz1 && w0) { // Compare with backward neighbor. + const int u = map(x,y,z - 1,0), v = map(x,y,z - 1,1), w = map(x,y,z - 1,2); + if (u>=cx1 && u=cy1 && v=cz1 - 1 && w=cx1 + 1 && u=cy1 && v=cz1 && w=cx1 && u=cy1 + 1 && v=cz1 && w=cx1 && u=cy1 && v=cz1 + 1 && w::inf()); + } else cimg_forXY(*this,x,y) { // Random initialization. + const int + cx1 = x<=psizew1?x:(x::inf()); + } + + // Start iteration loop. + for (unsigned int iter = 0; iter64 && iter0) { // Compare with left neighbor. + const int u = map(x - 1,y,0), v = map(x - 1,y,1); + if (u>=cx1 - 1 && u=cy1 && v0) { // Compare with up neighbor. + const int u = map(x,y - 1,0), v = map(x,y - 1,1); + if (u>=cx1 && u=cy1 - 1 && v=cx1 + 1 && u=cy1 && v=cx1 && u=cy1 + 1 && v& img1, const CImg& img2, + const unsigned int psizew, const unsigned int psizeh, + const int x1, const int y1, + const int x2, const int y2, + const float max_ssd) { // 2d version. + const T *p1 = img1.data(x1,y1), *p2 = img2.data(x2,y2); + const unsigned long + offx1 = (unsigned long)img1._width - psizew, + offx2 = (unsigned long)img2._width - psizew, + offy1 = (unsigned long)img1._width*img1._height - psizeh*img1._width, + offy2 = (unsigned long)img2._width*img2._height - psizeh*img2._width; + float ssd = 0; + cimg_forC(img1,c) { + for (unsigned int j = 0; jmax_ssd) return max_ssd; + p1+=offx1; p2+=offx2; + } + p1+=offy1; p2+=offy2; + } + return ssd; + } + + static float _patchmatch(const CImg& img1, const CImg& img2, + const unsigned int psizew, const unsigned int psizeh, const unsigned int psized, + const int x1, const int y1, const int z1, + const int x2, const int y2, const int z2, + const float max_ssd) { // 3d version. + const T *p1 = img1.data(x1,y1,z1), *p2 = img2.data(x2,y2,z2); + const unsigned long + offx1 = (unsigned long)img1._width - psizew, + offx2 = (unsigned long)img2._width - psizew, + offy1 = (unsigned long)img1._width*img1._height - psizeh*img1._width - psizew, + offy2 = (unsigned long)img2._width*img2._height - psizeh*img2._width - psizew, + offz1 = (unsigned long)img1._width*img1._height*img1._depth - psized*img1._width*img1._height - + psizeh*img1._width - psizew, + offz2 = (unsigned long)img2._width*img2._height*img2._depth - psized*img2._width*img2._height - + psizeh*img2._width - psizew; + float ssd = 0; + cimg_forC(img1,c) { + for (unsigned int k = 0; kmax_ssd) return max_ssd; + p1+=offx1; p2+=offx2; + } + p1+=offy1; p2+=offy2; + } + p1+=offz1; p2+=offz2; + } + return ssd; + } + //! Compute Euclidean distance function to a specified value. /** \param value Reference value. @@ -28388,7 +32768,7 @@ namespace cimg_library_suffixed { const unsigned long wh = (unsigned long)_width*_height; #if defined(cimg_use_openmp) && !cimg_is_gcc49x -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { CImg g(_width), dt(_width), s(_width), t(_width); @@ -28441,7 +32821,7 @@ namespace cimg_library_suffixed { if (!is_value) return fill(cimg::type::max()); const unsigned long wh = (unsigned long)_width*_height; #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) #endif cimg_forC(*this,c) { CImg img = get_shared_channel(c); @@ -28491,7 +32871,7 @@ namespace cimg_library_suffixed { return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); } - //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance. template CImg::type> get_distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, @@ -28695,7 +33075,7 @@ namespace cimg_library_suffixed { CImg state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen. #ifdef cimg_use_openmp -#pragma omp parallel for if (_spectrum>=2) firstprivate(Q,state) +#pragma omp parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state) #endif cimg_forC(*this,c) { const CImg img = get_shared_channel(c); @@ -29645,7 +34025,7 @@ namespace cimg_library_suffixed { **/ template const CImg& texturize_object3d(CImgList& primitives, CImgList& colors, - const CImg& texture, const CImg& coords=CImg::empty()) const { + const CImg& texture, const CImg& coords=CImg::const_empty()) const { if (is_empty()) return *this; if (_height!=3) throw CImgInstanceException(_cimg_instance @@ -30551,7 +34931,9 @@ namespace cimg_library_suffixed { struct _functor2d_expr { _cimg_math_parser *mp; - _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,0); } + _functor2d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } ~_functor2d_expr() { delete mp; } float operator()(const float x, const float y) const { return (float)(*mp)(x,y,0,0); @@ -30577,7 +34959,9 @@ namespace cimg_library_suffixed { struct _functor3d_expr { _cimg_math_parser *mp; ~_functor3d_expr() { delete mp; } - _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg::empty(),expr,0); } + _functor3d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } float operator()(const float x, const float y, const float z) const { return (float)(*mp)(x,y,z,0); } @@ -35102,10 +39486,10 @@ namespace cimg_library_suffixed { const t *ptrs = sprite._data - (bx?x0:0) - - (by?y0*sprite.width():0) - - (bz?z0*sprite.width()*sprite.height():0) - - (bc?c0*sprite.width()*sprite.height()*sprite.depth():0); - const unsigned long + (by?y0*(uptrT)sprite.width():0) - + (bz?z0*(uptrT)sprite.width()*sprite.height():0) - + (bc?c0*(uptrT)sprite.width()*sprite.height()*sprite.depth():0); + const uptrT offX = (unsigned long)_width - lX, soffX = (unsigned long)sprite._width - lX, offY = (unsigned long)_width*(_height - lY), @@ -35146,9 +39530,9 @@ namespace cimg_library_suffixed { const T *ptrs = sprite._data - (bx?x0:0) - - (by?y0*sprite.width():0) - - (bz?z0*sprite.width()*sprite.height():0) - - (bc?c0*sprite.width()*sprite.height()*sprite.depth():0); + (by?y0*(uptrT)sprite.width():0) - + (bz?z0*(uptrT)sprite.width()*sprite.height():0) - + (bc?c0*(uptrT)sprite.width()*sprite.height()*sprite.depth():0); const unsigned long offX = (unsigned long)_width - lX, soffX = (unsigned long)sprite._width - lX, @@ -35238,12 +39622,14 @@ namespace cimg_library_suffixed { lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0), lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0), lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0); - const int - coff = -(bx?x0:0) - (by?y0*mask.width():0) - (bz?z0*mask.width()*mask.height():0) - - (bc?c0*mask.width()*mask.height()*mask.depth():0), - ssize = mask.width()*mask.height()*mask.depth()*mask.spectrum(); + const uptrT + coff = -(bx?x0:0) - + (by?y0*(uptrT)mask.width():0) - + (bz?z0*(uptrT)mask.width()*mask.height():0) - + (bc?c0*(uptrT)mask.width()*mask.height()*mask.depth():0), + ssize = (uptrT)mask.width()*mask.height()*mask.depth()*mask.spectrum(); const ti *ptrs = sprite._data + coff; - const tm *ptrm = mask._data + coff; + const tm *ptrm = mask._data + coff; const unsigned long offX = (unsigned long)_width - lX, soffX = (unsigned long)sprite._width - lX, @@ -35568,32 +39954,32 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height; const int siz = (int)values_x.size() - 1; - char txt[32] = { 0 }; + CImg txt(32); CImg label; if (siz<=0) { // Degenerated case. draw_line(0,y,_width - 1,y,color,opacity,pattern); if (!siz) { - cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_x); + cimg_snprintf(txt,txt._width,"%g",(double)*values_x); label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); const int _xt = (width() - label.width())/2, xt = _xt<3?3:_xt + label.width()>=width() - 2?width() - 3 - label.width():_xt; draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity); - if (allow_zero || txt[0]!='0' || txt[1]!=0) + if (allow_zero || *txt!='0' || txt[1]!=0) draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); } } else { // Regular case. if (values_x[0]=width() - 2?width() - 3 - label.width():_xt; draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity); - if (allow_zero || txt[0]!='0' || txt[1]!=0) + if (allow_zero || *txt!='0' || txt[1]!=0) draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); } } @@ -35617,12 +40003,12 @@ namespace cimg_library_suffixed { const bool allow_zero=true) { if (is_empty()) return *this; int siz = (int)values_y.size() - 1; - char txt[32] = { 0 }; + CImg txt(32); CImg label; if (siz<=0) { // Degenerated case. draw_line(x,0,x,_height - 1,color,opacity,pattern); if (!siz) { - cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_y); + cimg_snprintf(txt,txt._width,"%g",(double)*values_y); label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); const int _yt = (height() - label.height())/2, @@ -35630,14 +40016,14 @@ namespace cimg_library_suffixed { _xt = x - 2 - label.width(), xt = _xt>=0?_xt:x + 3; draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity); - if (allow_zero || txt[0]!='0' || txt[1]!=0) + if (allow_zero || *txt!='0' || txt[1]!=0) draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); } } else { // Regular case. if (values_y[0]=0?_xt:x + 3; draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity); - if (allow_zero || txt[0]!='0' || txt[1]!=0) + if (allow_zero || *txt!='0' || txt[1]!=0) draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); } } @@ -36186,7 +40572,8 @@ namespace cimg_library_suffixed { for (int y0 = 0; y0M?M:val); } @@ -36195,21 +40582,24 @@ namespace cimg_library_suffixed { for (int x0=0; x0M?M:val); } for (int y0 = 0; y0M?M:val); } for (int y = -delta2; yM?M:val); } } @@ -36592,7 +40982,7 @@ namespace cimg_library_suffixed { const bool is_double_sided=false, const float focale=700, const float lightx=0, const float lighty=0, const float lightz=-5e8, const float specular_lightness=0.2f, const float specular_shininess=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), render_type,is_double_sided,focale,lightx,lighty,lightz, specular_lightness,specular_shininess,CImg::empty()); } @@ -36607,7 +40997,7 @@ namespace cimg_library_suffixed { const float lightx, const float lighty, const float lightz, const float specular_lightness, const float specular_shininess, CImg& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), render_type,is_double_sided,focale,lightx,lighty,lightz, specular_lightness,specular_shininess,zbuffer); } @@ -36622,7 +41012,7 @@ namespace cimg_library_suffixed { const bool is_double_sided=false, const float focale=700, const float lightx=0, const float lighty=0, const float lightz=-5e8, const float specular_lightness=0.2f, const float specular_shininess=0.1f) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), render_type,is_double_sided,focale,lightx,lighty,lightz, specular_lightness,specular_shininess,CImg::empty()); } @@ -36637,7 +41027,7 @@ namespace cimg_library_suffixed { const float lightx, const float lighty, const float lightz, const float specular_lightness, const float specular_shininess, CImg& zbuffer) { - return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::empty(), + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), render_type,is_double_sided,focale,lightx,lighty,lightz, specular_lightness,specular_shininess,zbuffer); } @@ -36755,7 +41145,7 @@ namespace cimg_library_suffixed { const float absfocale = focale?cimg::abs(focale):0; if (absfocale) { #ifdef cimg_use_openmp -#pragma omp parallel for if (projections.size()>4096) +#pragma omp parallel for cimg_openmp_if(projections.size()>4096) #endif cimg_forX(projections,l) { // Perspective projection const tpfloat @@ -36769,7 +41159,7 @@ namespace cimg_library_suffixed { } else { #ifdef cimg_use_openmp -#pragma omp parallel for if (projections.size()>4096) +#pragma omp parallel for cimg_openmp_if(projections.size()>4096) #endif cimg_forX(projections,l) { // Parallel projection const tpfloat @@ -36792,7 +41182,7 @@ namespace cimg_library_suffixed { bool is_forward = zbuffer?true:false; #ifdef cimg_use_openmp -#pragma omp parallel for if (primitives.size()>4096) +#pragma omp parallel for cimg_openmp_if(primitives.size()>4096) #endif cimglist_for(primitives,l) { const CImg& primitive = primitives[l]; @@ -36943,7 +41333,7 @@ namespace cimg_library_suffixed { case 3 : { // Flat Shading lightprops.assign(nb_visibles); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) #endif cimg_forX(lightprops,l) { const CImg& primitive = primitives(visibles(permutations(l))); @@ -36977,7 +41367,7 @@ namespace cimg_library_suffixed { case 5 : { // Phong-Shading CImg vertices_normals(vertices._width,6,1,1,0); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) #endif for (unsigned int l = 0; l& primitive = primitives[visibles(l)]; @@ -37030,7 +41420,7 @@ namespace cimg_library_suffixed { if (render_type==4) { lightprops.assign(vertices._width); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) #endif cimg_forX(lightprops,l) { const tpfloat @@ -37051,7 +41441,7 @@ namespace cimg_library_suffixed { lh2 = light_texture._height/2 - 1; lightprops.assign(vertices._width,2); #ifdef cimg_use_openmp -#pragma omp parallel for if (nb_visibles>4096) +#pragma omp parallel for cimg_openmp_if(nb_visibles>4096) #endif cimg_forX(lightprops,l) { const tpfloat @@ -37864,32 +42254,37 @@ namespace cimg_library_suffixed { \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images. **/ CImg& select(CImgDisplay &disp, - const unsigned int feature_type=2, unsigned int *const XYZ=0) { - return get_select(disp,feature_type,XYZ).move_to(*this); + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) { + return get_select(disp,feature_type,XYZ,exit_on_anykey).move_to(*this); } //! Simple interface to select a shape from an image \overloading. CImg& select(const char *const title, - const unsigned int feature_type=2, unsigned int *const XYZ=0) { - return get_select(title,feature_type,XYZ).move_to(*this); + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) { + return get_select(title,feature_type,XYZ,exit_on_anykey).move_to(*this); } //! Simple interface to select a shape from an image \newinstance. CImg get_select(CImgDisplay &disp, - const unsigned int feature_type=2, unsigned int *const XYZ=0) const { - return _get_select(disp,0,feature_type,XYZ,0,0,0,true,false); + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + return _get_select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false); } //! Simple interface to select a shape from an image \newinstance. CImg get_select(const char *const title, - const unsigned int feature_type=2, unsigned int *const XYZ=0) const { + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; - return _get_select(disp,title,feature_type,XYZ,0,0,0,true,false); + return _get_select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false); } CImg _get_select(CImgDisplay &disp, const char *const title, const unsigned int feature_type, unsigned int *const XYZ, const int origX, const int origY, const int origZ, + const bool exit_on_anykey, const bool reset_view3d, const bool force_display_z_coord) const { if (is_empty()) return CImg(1,feature_type==0?3:6,1,1,-1); @@ -37898,13 +42293,18 @@ namespace cimg_library_suffixed { if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); } else if (title) disp.set_title("%s",title); + CImg thumb; + if (width()>disp.screen_width() || height()>disp.screen_height()) { + const double ratio = cimg::min((double)disp.screen_width()/width(),(double)disp.screen_height()/height()); + get_resize(cimg::max(1,(int)(ratio*width())),cimg::max(1,(int)(ratio*height())),-100,-100).move_to(thumb); + } + const unsigned int old_normalization = disp.normalization(); bool old_is_resized = disp.is_resized(); disp._normalization = 0; disp.show().set_key(0).set_wheel().show_mouse(); - disp._mouse_x = disp._mouse_y = -1; - unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; int area = 0, starting_area = 0, clicked_area = 0, phase = 0, X0 = (int)((XYZ?XYZ[0]:(_width - 1)/2)%_width), @@ -37945,6 +42345,8 @@ namespace cimg_library_suffixed { if (mX>=width() && mY>=height()) area = 4; if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0; + CImg filename(32); + switch (key = disp.key()) { #if cimg_OS!=2 case cimg::keyCTRLRIGHT : @@ -37982,35 +42384,33 @@ namespace cimg_library_suffixed { } break; case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); if (visu0) { (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp); visu0.save(filename); - (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,13,filename). + (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data). display(disp); } disp.set_key(key,false); key = 0; } break; case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp); save(filename); - (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename). + (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data). display(disp); disp.set_key(key,false); key = 0; } break; @@ -38043,7 +42443,7 @@ namespace cimg_library_suffixed { X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; } } - if (disp.button()&4) { // Reset positions. + if (disp.button()&4) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = clicked_area = starting_area = 0; visu0.assign(); } @@ -38146,7 +42546,9 @@ namespace cimg_library_suffixed { if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { if (!visu0) { // Create image of projected planes. - __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0).resize(disp); + if (thumb) thumb.__get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + else __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + visu0.resize(disp); view3d.assign(); points3d.assign(); } @@ -38409,6 +42811,10 @@ namespace cimg_library_suffixed { } else if (!shape_selected) disp.wait(); if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); } omx = mx; omy = my; + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } } // Return result. @@ -38428,7 +42834,7 @@ namespace cimg_library_suffixed { default : res[0] = X0; res[1] = Y0; res[2] = Z0; } } - disp.set_button(); + if (!exit_on_anykey || !(disp.button()&4)) disp.set_button(); if (!visible_cursor) disp.show_mouse(); disp._normalization = old_normalization; disp._is_resized = old_is_resized; @@ -38445,9 +42851,8 @@ namespace cimg_library_suffixed { if (_depth>1) crop.get_projections2d(x,y,z).move_to(img2d); else CImg(crop,false).move_to(img2d); - // Check for inf and nan values. - if (cimg::type::is_float() && disp._normalization && - (disp._normalization!=3 || cimg::type::string()!=cimg::type::string())) { + // Check for inf and NaN values. + if (cimg::type::is_float() && normalization) { bool is_inf = false, is_nan = false; cimg_for(img2d,ptr,Tuchar) if (cimg::type::is_inf(*ptr)) { is_inf = true; break; } @@ -38467,7 +42872,7 @@ namespace cimg_library_suffixed { val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0); if (is_nan) cimg_for(img2d,ptr,Tuchar) - if (cimg::type::is_nan(*ptr)) *ptr = val_minf; // Replace nan values. + if (cimg::type::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values. if (is_inf) cimg_for(img2d,ptr,Tuchar) if (cimg::type::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values. @@ -38496,7 +42901,8 @@ namespace cimg_library_suffixed { CImg get_select_graph(CImgDisplay &disp, const unsigned int plot_type=1, const unsigned int vertex_type=1, const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0) const { + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { if (is_empty()) throw CImgInstanceException(_cimg_instance "select_graph(): Empty instance.", @@ -38512,8 +42918,8 @@ namespace cimg_library_suffixed { if (nymin==nymax) { --nymin; ++nymax; } if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; } - const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; - const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 }; + static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; + static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 }; static unsigned int odimv = 0; static CImg colormap; if (odimv!=_spectrum) { @@ -38619,8 +43025,8 @@ namespace cimg_library_suffixed { (double)(*this)(x,0,0,_spectrum - 1)); else { cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx); - cimg_forC(*this,c) std::sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); - std::sprintf(message._data + std::strlen(message),")"); + cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); + cimg_sprintf(message._data + std::strlen(message),")"); } if (x0>=0 && x1>=0) { const unsigned int @@ -38634,10 +43040,10 @@ namespace cimg_library_suffixed { cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32), cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32); if (y0>=0 && y1>=0) - std::sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", + cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", x0,cx0,cy0,x1 + one,cx1,cy1); else - std::sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", + cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", x0,cx0,x1 + one,cx1); } text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3); @@ -38647,6 +43053,7 @@ namespace cimg_library_suffixed { } // Test keys. + CImg filename(32); switch (okey = key) { #if cimg_OS!=2 case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : @@ -38678,15 +43085,14 @@ namespace cimg_library_suffixed { static unsigned int snap_number = 0; if (visu || visu0) { CImg &screen = visu?visu:visu0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp); screen.save(filename); - (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp); + (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename._data).display(disp); } disp.set_key(key,false); okey = 0; } break; @@ -38694,19 +43100,18 @@ namespace cimg_library_suffixed { static unsigned int snap_number = 0; if (visu || visu0) { CImg &screen = visu?visu:visu0; - char filename[32] = { 0 }; std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp); save(filename); - (+screen).draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename).display(disp); + (+screen).draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename._data).display(disp); } disp.set_key(key,false); okey = 0; } break; @@ -38733,6 +43138,11 @@ namespace cimg_library_suffixed { } if (disp.is_resized()) { disp.resize(false); visu0.assign(); } if (visu && visu0) disp.wait(); + if (!exit_on_anykey && okey && okey!=cimg::keyESC && + (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + okey = 0; + } } disp._normalization = old_normalization; @@ -39067,8 +43477,8 @@ namespace cimg_library_suffixed { cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - unsigned char header[64] = { 0 }; - cimg::fread(header,54,nfile); + CImg header(54); + cimg::fread(header._data,54,nfile); if (*header!='B' || header[1]!='M') { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimg_instance @@ -39093,7 +43503,7 @@ namespace cimg_library_suffixed { file_size = (int)std::ftell(nfile); std::fseek(nfile,54,SEEK_SET); } - if (header_size>40) std::fseek(nfile, header_size - 40, SEEK_CUR); + if (header_size>40) std::fseek(nfile,header_size - 40,SEEK_CUR); const int cimg_iobuffer = 24*1024*1024, @@ -40037,7 +44447,7 @@ 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 compilation time the + - If \c cimg_use_tif is not defined at compile time the function uses CImg& load_other(const char*). **/ CImg& load_tiff(const char *const filename, @@ -40243,10 +44653,13 @@ namespace cimg_library_suffixed { if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) CImg::string(s_description).move_to(*description); } - const unsigned int spectrum = is_spp?samplesperpixel:photo==3?3:1; + const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel; assign(nx,ny,1,spectrum); - if (photo>=3 && sampleformat==1 && bitspersample==8 && (samplesperpixel==3 || samplesperpixel==4)) { + if ((photo>=3 && sampleformat==1 && + (bitspersample==4 || bitspersample==8) && + (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) || + (bitspersample==1 && samplesperpixel==1)) { // Special case for unsigned color images. uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32)); if (!raster) { @@ -40258,24 +44671,25 @@ namespace cimg_library_suffixed { } TIFFReadRGBAImage(tif,nx,ny,raster,0); switch (spectrum) { - case 1 : { - cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny - 1 - y) + x] + 128)/257); - } break; - case 3 : { + case 1 : + cimg_forXY(*this,x,y) + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); + break; + case 3 : cimg_forXY(*this,x,y) { (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 -y) + x]); (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 -y) + x]); } - } break; - case 4 : { + break; + case 4 : cimg_forXY(*this,x,y) { (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]); } - } break; + break; } _TIFFfree(raster); } else { // Other cases. @@ -40432,11 +44846,11 @@ namespace cimg_library_suffixed { const char *const ext = cimg::split_filename(filename,body); if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file. nfile_header = cimg::fopen(filename,"rb"); - std::sprintf(body._data + std::strlen(body),".img"); + cimg_sprintf(body._data + std::strlen(body),".img"); nfile = cimg::fopen(body,"rb"); } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file. nfile = cimg::fopen(filename,"rb"); - std::sprintf(body._data + std::strlen(body),".hdr"); + cimg_sprintf(body._data + std::strlen(body),".hdr"); nfile_header = cimg::fopen(body,"rb"); } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file. } else nfile_header = nfile = file; // File is a Niftii file. @@ -40452,7 +44866,7 @@ namespace cimg_library_suffixed { cimg::fread(&header_size,1,nfile_header); if (!header_size) throw CImgIOException(_cimg_instance - "load_analyze(): Invalid zero-sized header in file '%s'.", + "load_analyze(): Invalid zero-size header in file '%s'.", cimg_instance, filename?filename:"(FILE*)"); @@ -40661,8 +45075,8 @@ namespace cimg_library_suffixed { } static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { - CImg item(1024); *item = 0; - char tmp1[64] = { 0 }, tmp2[64] = { 0 }; + CImg item(1024), tmp1(64), tmp2(64); + *item = *tmp1 = *tmp2 = 0; out[0] = std::fscanf(file,"%63s",item._data); out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) @@ -40680,19 +45094,19 @@ namespace cimg_library_suffixed { cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1); cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2); } - if (cimg_sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1; - switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) { + if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1; + switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) { case 0 : break; - case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,sizeof(tmp1) - 1); + case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,tmp1._width - 1); case 1 : - if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; + if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; - if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; + if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; if (out[4]>=0) break; default : throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", pixel_type(), - tmp2); + tmp2._data); } } if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) @@ -40846,8 +45260,8 @@ namespace cimg_library_suffixed { cimg_instance); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); - char header[32] = { 0 }; - cimg::fread(header,12,nfile); + CImg header(32); + cimg::fread(header._data,12,nfile); if (cimg::strncasecmp("PANDORE",header,7)) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimg_instance @@ -40856,10 +45270,11 @@ namespace cimg_library_suffixed { filename?filename:"(FILE*)"); } unsigned int imageid, dims[8] = { 0 }; + int ptbuf[4] = { 0 }; cimg::fread(&imageid,1,nfile); - const bool endian = (imageid>255); + const bool endian = imageid>255; if (endian) cimg::invert_endianness(imageid); - cimg::fread(header,20,nfile); + cimg::fread(header._data,20,nfile); switch (imageid) { case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break; @@ -40991,19 +45406,16 @@ namespace cimg_library_suffixed { break; case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break; case 34 : { // Points 1d - int ptbuf[4] = { 0 }; cimg::fread(ptbuf,1,nfile); if (endian) cimg::invert_endianness(ptbuf,1); assign(1); (*this)(0) = (T)ptbuf[0]; } break; case 35 : { // Points 2d - int ptbuf[4] = { 0 }; cimg::fread(ptbuf,2,nfile); if (endian) cimg::invert_endianness(ptbuf,2); assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; } break; case 36 : { // Points 3d - int ptbuf[4] = { 0 }; cimg::fread(ptbuf,3,nfile); if (endian) cimg::invert_endianness(ptbuf,3); assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; @@ -41886,11 +46298,11 @@ namespace cimg_library_suffixed { const unsigned long siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1, mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1; - char _title[64] = { 0 }; - if (!title) cimg_snprintf(_title,sizeof(_title),"CImg<%s>",pixel_type()); + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type()); std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p", - cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal, + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, cimg::t_bold,cimg::t_normal,(void*)this, cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum, mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), @@ -41934,8 +46346,9 @@ namespace cimg_library_suffixed { \param disp Display window. \param display_info Tells if image information are displayed on the standard output. **/ - const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0) const { - return _display(disp,0,display_info,XYZ,false); + const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + return _display(disp,0,display_info,XYZ,exit_on_anykey,false); } //! Display image into an interactive window. @@ -41943,16 +46356,18 @@ namespace cimg_library_suffixed { \param title Window title \param display_info Tells if image information are displayed on the standard output. **/ - const CImg& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0) const { + const CImg& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; - return _display(disp,title,display_info,XYZ,false); + return _display(disp,title,display_info,XYZ,exit_on_anykey,false); } - const CImg& _display(CImgDisplay &disp, const char *const title, - const bool display_info, unsigned int *const XYZ, + const CImg& _display(CImgDisplay &disp, const char *const title, const bool display_info, + unsigned int *const XYZ, const bool exit_on_anykey, const bool exit_on_simpleclick) const { unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0; - int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1; + int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1, + old_mouse_x = -1, old_mouse_y = -1; if (!disp) { disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); @@ -42010,14 +46425,21 @@ namespace cimg_library_suffixed { _XYZ[1] = (unsigned int)(y1 - y0)/2; _XYZ[2] = (unsigned int)(z1 - z0)/2; } - const CImg selection = visu._get_select(disp,0,2,_XYZ,x0,y0,z0,is_first_select,_depth>1); + + disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; + const CImg selection = visu._get_select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1); + old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y; is_first_select = false; if (disp.wheel()) { if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + go_down = !(go_up = disp.wheel()>0); + } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { + go_left = !(go_right = disp.wheel()>0); + } + else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; - } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_left = !(go_right = disp.wheel()>0); } - else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); } + } disp.set_wheel(); } @@ -42175,6 +46597,10 @@ namespace cimg_library_suffixed { else { z0+=(depth() - 1 - z1); z1 = depth() - 1; } } disp.wait(100); + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } } disp.set_key(key); if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; } @@ -42212,11 +46638,12 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static, render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -42231,12 +46658,13 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static, render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -42250,11 +46678,12 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return display_object3d(disp,vertices,primitives,colors,CImgList(),centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -42268,11 +46697,12 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return display_object3d(title,vertices,primitives,colors,CImgList(),centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -42285,11 +46715,12 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return display_object3d(disp,vertices,primitives,CImgList(),centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } @@ -42303,11 +46734,12 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return display_object3d(title,vertices,primitives,CImgList(),centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -42319,11 +46751,12 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return display_object3d(disp,vertices,CImgList(),centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } //! Display object 3d in an interactive window \simplification. @@ -42335,11 +46768,12 @@ namespace cimg_library_suffixed { const bool is_double_sided=true, const float focale=700, const float light_x=0, const float light_y=0, const float light_z=-5e8f, const float specular_lightness=0.2f, const float specular_shininess=0.1f, - const bool display_axes=true, float *const pose_matrix=0) const { + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { return display_object3d(title,vertices,CImgList(),centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } template @@ -42353,7 +46787,8 @@ namespace cimg_library_suffixed { const bool is_double_sided, const float focale, const float light_x, const float light_y, const float light_z, const float specular_lightness, const float specular_shininess, - const bool display_axes, float *const pose_matrix) const { + const bool display_axes, float *const pose_matrix, + const bool exit_on_anykey) const { typedef typename cimg::superset::type tpfloat; // Check input arguments @@ -42362,14 +46797,14 @@ namespace cimg_library_suffixed { _display_object3d(disp,title,vertices,primitives,colors,opacities,centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); else return CImg(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2, CImgDisplay::screen_height()/2,1), 1,(colors && colors[0].size()==1)?1:3,3). _display_object3d(disp,title,vertices,primitives,colors,opacities,centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } else { if (disp) disp.resize(*this,false); } CImg error_message(1024); if (!vertices.is_object3d(primitives,colors,opacities,true,error_message)) @@ -42382,7 +46817,7 @@ namespace cimg_library_suffixed { return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, render_static,render_motion,is_double_sided,focale, light_x,light_y,light_z,specular_lightness,specular_shininess, - display_axes,pose_matrix); + display_axes,pose_matrix,exit_on_anykey); } if (!disp) { disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); @@ -42583,6 +47018,7 @@ namespace cimg_library_suffixed { } } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } + CImg filename(32); switch (key = disp.key()) { #if cimg_OS!=2 case cimg::keyCTRLRIGHT : @@ -42661,43 +47097,40 @@ namespace cimg_library_suffixed { } break; case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).draw_text(0,0," Saving snapshot... ", foreground_color._data,background_color._data,0.7f,13).display(disp); visu.save(filename); (+visu).draw_text(0,0," Snapshot '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp); disp.set_key(key,false); key = 0; } break; case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.off",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++); if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).draw_text(0,0," Saving object... ", foreground_color._data,background_color._data,0.7f,13).display(disp); vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename); (+visu).draw_text(0,0," Object '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp); disp.set_key(key,false); key = 0; } break; case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); @@ -42706,16 +47139,15 @@ namespace cimg_library_suffixed { vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities). save(filename); (+visu).draw_text(0,0," Object '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp); disp.set_key(key,false); key = 0; } break; #ifdef cimg_use_board case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.eps",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++); if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).draw_text(0,0," Saving EPS snapshot... ", @@ -42731,15 +47163,14 @@ namespace cimg_library_suffixed { sprite_scale); board.saveEPS(filename); (+visu).draw_text(0,0," Object '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp); disp.set_key(key,false); key = 0; } break; case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.svg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++); if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).draw_text(0,0," Saving SVG snapshot... ", @@ -42755,7 +47186,7 @@ namespace cimg_library_suffixed { sprite_scale); board.saveSVG(filename); (+visu).draw_text(0,0," Object '%s' saved. ", - foreground_color._data,background_color._data,0.7f,13,filename).display(disp); + foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp); disp.set_key(key,false); key = 0; } break; #endif @@ -42765,6 +47196,10 @@ namespace cimg_library_suffixed { if (zbuffer) zbuffer.assign(disp.width(),disp.height()); redraw = true; } + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } } if (pose_matrix) { std::memcpy(pose_matrix,pose._data,12*sizeof(float)); @@ -42789,23 +47224,26 @@ namespace cimg_library_suffixed { const CImg& display_graph(CImgDisplay &disp, const unsigned int plot_type=1, const unsigned int vertex_type=1, const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0) const { - return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); } //! Display 1d graph in an interactive window \overloading. const CImg& display_graph(const char *const title=0, const unsigned int plot_type=1, const unsigned int vertex_type=1, const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0) const { + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; - return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); + return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); } const CImg& _display_graph(CImgDisplay &disp, const char *const title=0, - const unsigned int plot_type=1, const unsigned int vertex_type=1, - const char *const labelx=0, const double xmin=0, const double xmax=0, - const char *const labely=0, const double ymin=0, const double ymax=0) const { + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { if (is_empty()) throw CImgInstanceException(_cimg_instance "display_graph(): Empty instance.", @@ -42831,7 +47269,7 @@ namespace cimg_library_suffixed { labelx, nxmin + x0*(nxmax - nxmin)/siz1, nxmin + x1*(nxmax - nxmin)/siz1, - labely,y0,y1); + labely,y0,y1,true); const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); if (selection[0]>=0) { if (selection[2]<0) reset_view = true; @@ -42860,9 +47298,9 @@ namespace cimg_library_suffixed { case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break; } if (disp.wheel()) { - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_out = !(go_in = disp.wheel()>0); + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0); else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0); - else go_up = !(go_down = disp.wheel()<0); + else go_out = !(go_in = disp.wheel()>0); key = 0; } @@ -42915,6 +47353,11 @@ namespace cimg_library_suffixed { go_down = false; } } + if (!exit_on_anykey && key && key!=(int)cimg::keyESC && + (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + key = 0; + } } disp._normalization = old_normalization; return *this; @@ -43176,7 +47619,8 @@ namespace cimg_library_suffixed { filename?filename:"(FILE*)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - unsigned char header[54] = { 0 }, align_buf[4] = { 0 }; + CImg header(54,1,1,1,0); + unsigned char align_buf[4] = { 0 }; const unsigned int align = (4 - (3*_width)%4)%4, buf_size = (3*_width + align)*height(), @@ -43206,7 +47650,7 @@ namespace cimg_library_suffixed { header[0x25] = (buf_size>>24)&0xFF; header[0x27] = 0x1; header[0x2B] = 0x1; - cimg::fwrite(header,54,nfile); + cimg::fwrite(header._data,54,nfile); const T *ptr_r = data(0,_height - 1,0,0), @@ -43286,7 +47730,7 @@ namespace cimg_library_suffixed { unsigned int dimbuf = 0; J_COLOR_SPACE colortype = JCS_RGB; - switch(_spectrum) { + switch (_spectrum) { case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break; case 2 : dimbuf = 3; colortype = JCS_RGB; break; case 3 : dimbuf = 3; colortype = JCS_RGB; break; @@ -43318,13 +47762,13 @@ namespace cimg_library_suffixed { switch (_spectrum) { case 1 : { // Greyscale images const T *ptr_g = data(0, cinfo.next_scanline); - for(unsigned int b = 0; b < cinfo.image_width; b++) + for (unsigned int b = 0; bchar,uchar,short,ushort,float and \c double pixel types. - - If \c cimg_use_tif is not defined at compilation time the + - If \c cimg_use_tif is not defined at compile time the function uses CImg&save_other(const char*). **/ const CImg& save_tiff(const char *const filename, const unsigned int compression_type=0, - const float *const voxel_size=0, const char *const description=0) const { + const float *const voxel_size=0, const char *const description=0, + const bool is_bigtiff=true) const { if (!filename) throw CImgArgumentException(_cimg_instance "save_tiff(): Specified filename is (null).", @@ -44099,9 +48544,9 @@ namespace cimg_library_suffixed { if (is_empty()) { cimg::fempty(0,filename); return *this; } #ifdef cimg_use_tiff - TIFF *tif = TIFFOpen(filename,"w"); + TIFF *tif = TIFFOpen(filename,is_bigtiff?"w8":"w4"); if (tif) { - cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z,compression_type,voxel_size,description); + cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description); TIFFClose(tif); } else throw CImgIOException(_cimg_instance "save_tiff(): Failed to open file '%s' for writing.", @@ -44109,7 +48554,7 @@ namespace cimg_library_suffixed { filename); return *this; #else - cimg::unused(compression_type,voxel_size,description); + cimg::unused(compression_type,voxel_size,description,is_bigtiff); return save_other(filename); #endif } @@ -44117,13 +48562,13 @@ 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,foo,compression_type,voxel_size,description); } + const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); } // [internal] Save a plane into a tiff file template - const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t, - const unsigned int compression_type, - const float *const voxel_size, const char *const description) const { + const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { if (is_empty() || !tif || pixel_t) return *this; const char *const filename = TIFFFileName(tif); uint32 rowsperstrip = (uint32)-1; @@ -44166,7 +48611,7 @@ namespace cimg_library_suffixed { for (unsigned int rr = 0; rr& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int compression_type, - const float *const voxel_size, const char *const description) const { + const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { _cimg_save_tiff("bool",unsigned char,compression_type); _cimg_save_tiff("char",char,compression_type); _cimg_save_tiff("unsigned char",unsigned char,compression_type); @@ -44255,11 +48701,9 @@ namespace cimg_library_suffixed { if (is_empty()) { cimg::fempty(0,filename); return *this; } std::FILE *file; - char header[348] = { 0 }; - CImg hname(1024), iname(1024); + CImg header(348,1,1,1,0), hname(1024), iname(1024); const char *const ext = cimg::split_filename(filename); short datatype = -1; - std::memset(header,0,348); if (!*ext) { cimg_snprintf(hname,hname._width,"%s.hdr",filename); cimg_snprintf(iname,iname._width,"%s.img",filename); @@ -44267,27 +48711,27 @@ namespace cimg_library_suffixed { if (!cimg::strncasecmp(ext,"hdr",3)) { std::strcpy(hname,filename); std::strncpy(iname,filename,iname._width - 1); - std::sprintf(iname._data + std::strlen(iname) - 3,"img"); + cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); } if (!cimg::strncasecmp(ext,"img",3)) { std::strcpy(hname,filename); std::strncpy(iname,filename,iname._width - 1); - std::sprintf(hname._data + std::strlen(iname) - 3,"hdr"); + cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); } if (!cimg::strncasecmp(ext,"nii",3)) { std::strncpy(hname,filename,hname._width - 1); *iname = 0; } - int *const iheader = (int*)header; + int *const iheader = (int*)header._data; *iheader = 348; - std::strcpy(header + 4,"CImg"); - std::strcpy(header + 14," "); - ((short*)(header + 36))[0] = 4096; - ((char*)(header + 38))[0] = 114; - ((short*)(header + 40))[0] = 4; - ((short*)(header + 40))[1] = (short)_width; - ((short*)(header + 40))[2] = (short)_height; - ((short*)(header + 40))[3] = (short)_depth; - ((short*)(header + 40))[4] = (short)_spectrum; + std::strcpy(header._data + 4,"CImg"); + std::strcpy(header._data + 14," "); + ((short*)&(header[36]))[0] = 4096; + ((char*)&(header[38]))[0] = 114; + ((short*)&(header[40]))[0] = 4; + ((short*)&(header[40]))[1] = (short)_width; + ((short*)&(header[40]))[2] = (short)_height; + ((short*)&(header[40]))[3] = (short)_depth; + ((short*)&(header[40]))[4] = (short)_spectrum; if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; @@ -44305,17 +48749,17 @@ namespace cimg_library_suffixed { cimg_instance, pixel_type(),filename); - ((short*)(header + 70))[0] = datatype; - ((short*)(header + 72))[0] = sizeof(T); - ((float*)(header + 112))[0] = 1; - ((float*)(header + 76))[0] = 0; + ((short*)&(header[70]))[0] = datatype; + ((short*)&(header[72]))[0] = sizeof(T); + ((float*)&(header[112]))[0] = 1; + ((float*)&(header[76]))[0] = 0; if (voxel_size) { - ((float*)(header + 76))[1] = voxel_size[0]; - ((float*)(header + 76))[2] = voxel_size[1]; - ((float*)(header + 76))[3] = voxel_size[2]; - } else ((float*)(header + 76))[1] = ((float*)(header + 76))[2] = ((float*)(header + 76))[3] = 1; + ((float*)&(header[76]))[1] = voxel_size[0]; + ((float*)&(header[76]))[2] = voxel_size[1]; + ((float*)&(header[76]))[3] = voxel_size[2]; + } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1; file = cimg::fopen(hname,"wb"); - cimg::fwrite(header,348,file); + cimg::fwrite(header._data,348,file); if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } cimg::fwrite(_data,size(),file); cimg::fclose(file); @@ -44446,14 +48890,15 @@ namespace cimg_library_suffixed { pixel_type(),filename?filename:"(FILE*)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - char header[257] = { 0 }; - int err = cimg_snprintf(header,sizeof(header),"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", + CImg header(257); + int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", _width,_height,_depth,_spectrum); - if (voxel_size) err+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxel_size[0],voxel_size[1],voxel_size[2]); - err+=std::sprintf(header + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); - std::memset(header + err,'\n',252 - err); - std::memcpy(header + 252,"##}\n",4); - cimg::fwrite(header,256,nfile); + if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n", + voxel_size[0],voxel_size[1],voxel_size[2]); + err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); + std::memset(header._data + err,'\n',252 - err); + std::memcpy(header._data + 252,"##}\n",4); + cimg::fwrite(header._data,256,nfile); cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile); if (!file) cimg::fclose(nfile); return *this; @@ -44552,28 +48997,28 @@ namespace cimg_library_suffixed { unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const { unsigned int nbdims = 0; if (id==2 || id==3 || id==4) { - dims[0] = 1; dims[1] = _width; nbdims = 2; + dims[0] = 1; dims[1] = _width; nbdims = 2; } if (id==5 || id==6 || id==7) { - dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3; + dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3; } if (id==8 || id==9 || id==10) { - dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; } if (id==16 || id==17 || id==18) { - dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4; + dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4; } if (id==19 || id==20 || id==21) { - dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; + dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; } if (id==22 || id==23 || id==25) { - dims[0] = _spectrum; dims[1] = _width; nbdims = 2; + dims[0] = _spectrum; dims[1] = _width; nbdims = 2; } if (id==26 || id==27 || id==29) { - dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; + dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; } if (id==30 || id==31 || id==33) { - dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; } return nbdims; } @@ -44597,9 +49042,9 @@ namespace cimg_library_suffixed { cimg::fwrite(header,36,nfile); \ if (sizeof(unsigned long)==4) { CImg ndims(5); \ for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ - else if (sizeof(unsigned int)==4) { CImg ndims(5); \ + else if (sizeof(unsigned int)==4) { CImg ndims(5); \ for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ - else if (sizeof(unsigned short)==4) { CImg ndims(5); \ + else if (sizeof(unsigned short)==4) { CImg ndims(5); \ for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \ else throw CImgIOException(_cimg_instance \ "save_pandore(): Unsupported datatype for file '%s'.",\ @@ -44633,8 +49078,9 @@ namespace cimg_library_suffixed { if (is_empty()) { cimg::fempty(file,filename); return *this; } std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, - 0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 }; + static const unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, + 0,0,0,0,'C','I','m','g',0,0,0,0,0, + 'N','o',' ','d','a','t','e',0,0,0,0 }; unsigned int nbdims, dims[5] = { 0 }; bool saved = false; _cimg_save_pandore_case(1,1,1,"unsigned char",2); @@ -45921,8 +50367,7 @@ namespace cimg_library_suffixed { \note Can be used to exchange the content of two lists in a fast way. **/ CImgList& swap(CImgList& list) { - cimg::swap(_width,list._width); - cimg::swap(_allocated_width,list._allocated_width); + cimg::swap(_width,list._width,_allocated_width,list._allocated_width); cimg::swap(_data,list._data); return list; } @@ -45939,6 +50384,12 @@ namespace cimg_library_suffixed { return _empty.assign(); } + //! Return a reference to an empty list \const. + static const CImgList& const_empty() { + static const CImgList _empty; + return _empty; + } + //@} //------------------------------------------ // @@ -47480,8 +51931,9 @@ namespace cimg_library_suffixed { \return A one-column vector containing the selected image indexes. **/ CImg get_select(CImgDisplay &disp, const bool feature_type=true, - const char axis='x', const float align=0) const { - return _get_select(disp,0,feature_type,axis,align,0,false,false,false); + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { + return _get_select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false); } //! Display a simple interactive interface to select images or sublists. @@ -47493,13 +51945,14 @@ namespace cimg_library_suffixed { \return A one-column vector containing the selected image indexes. **/ CImg get_select(const char *const title, const bool feature_type=true, - const char axis='x', const float align=0) const { + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { CImgDisplay disp; - return _get_select(disp,title,feature_type,axis,align,0,false,false,false); + return _get_select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false); } CImg _get_select(CImgDisplay &disp, const char *const title, const bool feature_type, - const char axis, const float align, + const char axis, const float align, const bool exit_on_anykey, const unsigned int orig, const bool resize_disp, const bool exit_on_rightbutton, const bool exit_on_wheel) const { if (is_empty()) @@ -47538,7 +51991,7 @@ namespace cimg_library_suffixed { bool old_is_resized = disp.is_resized(); disp._normalization = 0; disp.show().set_key(0); - const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; // Enter event loop. CImg visu0, visu; @@ -47547,6 +52000,7 @@ namespace cimg_library_suffixed { int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1; bool is_clicked = false, is_selected = false, text_down = false, update_display = true; unsigned int key = 0; + while (!is_selected && !disp.is_closed() && !key) { // Create background image. @@ -47650,6 +52104,7 @@ namespace cimg_library_suffixed { if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; } if (disp.wheel() && exit_on_wheel) is_selected = true; + CImg filename(32); switch (key = disp.key()) { #if cimg_OS!=2 case cimg::keyCTRLRIGHT : @@ -47679,10 +52134,9 @@ namespace cimg_library_suffixed { } break; case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); if (visu0) { @@ -47690,20 +52144,19 @@ namespace cimg_library_suffixed { foreground_color,background_color,0.7f,13).display(disp); visu0.save(filename); (+visu0).draw_text(0,0," Snapshot '%s' saved. ", - foreground_color,background_color,0.7f,13,filename).display(disp); + foreground_color,background_color,0.7f,13,filename._data).display(disp); } disp.set_key(key,false).wait(); key = 0; } break; case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { static unsigned int snap_number = 0; - char filename[32] = { 0 }; std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); #else - cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); #endif if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); @@ -47711,13 +52164,17 @@ namespace cimg_library_suffixed { foreground_color,background_color,0.7f,13).display(disp); save(filename); (+visu0).draw_text(0,0," Instance '%s' saved. ", - foreground_color,background_color,0.7f,13,filename).display(disp); + foreground_color,background_color,0.7f,13,filename._data).display(disp); disp.set_key(key,false).wait(); key = 0; } break; } if (disp.is_resized()) { disp.resize(false); visu0.assign(); } if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }} else if (ym>=visu.height() - 13) { if(text_down) { visu.assign(); text_down = false; }} + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } } CImg res(1,2,1,1,-1); if (is_selected) { @@ -48422,7 +52879,7 @@ namespace cimg_library_suffixed { \param first_frame Index of the first frame to read. \param last_frame Index of the last frame to read. \param step_frame Step value for frame reading. - \note If step_frame==0, the current video stream is open or released without any frames read. + \note If step_frame==0, the current video stream is forced to be released (without any frames read). **/ CImgList& load_video(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, @@ -48465,7 +52922,7 @@ namespace cimg_library_suffixed { } else if (filename) cimg::warn(_cimglist_instance - "load_video() : File '%s', opened video stream associated with filename not found.", + "load_video() : File '%s', no opened video stream associated with filename found.", cimglist_instance,filename); else cimg::warn(_cimglist_instance @@ -48864,10 +53321,10 @@ namespace cimg_library_suffixed { cimglist_for(*this,l) msiz+=_data[l].size(); msiz*=sizeof(T); const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U; - char _title[64] = { 0 }; - if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type()); + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type()); std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p", - cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal, + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, cimg::t_bold,cimg::t_normal,(void*)this, cimg::t_bold,cimg::t_normal,_width,_allocated_width, mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), @@ -48915,9 +53372,9 @@ namespace cimg_library_suffixed { **/ const CImgList& display(CImgDisplay &disp, const bool display_info, const char axis='x', const float align=0, - unsigned int *const XYZ=0) const { + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { bool is_exit = false; - return _display(disp,0,display_info,axis,align,XYZ,0,true,is_exit); + return _display(disp,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); } //! Display the current CImgList instance in a new display window. @@ -48929,15 +53386,16 @@ namespace cimg_library_suffixed { **/ const CImgList& display(const char *const title=0, const bool display_info=true, const char axis='x', const float align=0, - unsigned int *const XYZ=0) const { + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { CImgDisplay disp; bool is_exit = false; - return _display(disp,title,display_info,axis,align,XYZ,0,true,is_exit); + return _display(disp,title,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); } const CImgList& _display(CImgDisplay &disp, const char *const title, const bool display_info, const char axis, const float align, unsigned int *const XYZ, - const unsigned int orig, const bool is_first_call, bool &is_exit) const { + const bool exit_on_anykey, const unsigned int orig, const bool is_first_call, + bool &is_exit) const { if (is_empty()) throw CImgInstanceException(_cimglist_instance "display(): Empty instance.", @@ -48978,13 +53436,13 @@ namespace cimg_library_suffixed { disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false). set_title("%s (%ux%ux%ux%u)", dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum); - _data[0]._display(disp,0,false,XYZ,!is_first_call); + _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call); if (disp.key()) is_exit = true; disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data()); } else { bool disp_resize = !is_first_call; while (!disp.is_closed() && !is_exit) { - const CImg s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true); + const CImg s = _get_select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true); disp_resize = true; if (s[0]<0) { // No selections done. if (disp.button()&2) { disp.flush(); break; } @@ -48999,10 +53457,12 @@ namespace cimg_library_suffixed { ind0 = (unsigned int)cimg::max(0,s[0] - (int)delta), ind1 = (unsigned int)cimg::min(width() - 1,s[0] + (int)delta); if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) - get_shared_images(ind0,ind1)._display(disp,0,false,axis,align,XYZ,orig + ind0,false,is_exit); + get_shared_images(ind0,ind1)._display(disp,0,false,axis,align,XYZ,exit_on_anykey, + orig + ind0,false,is_exit); } } else if (s[0]!=0 || s[1]!=width() - 1) - get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,XYZ,orig + s[0],false,is_exit); + get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,XYZ,exit_on_anykey, + orig + s[0],false,is_exit); } } return *this; @@ -49136,7 +53596,7 @@ namespace cimg_library_suffixed { \param fps Number of desired frames per second. \param nb_loops Number of loops (\c 0 for infinite looping). **/ - const CImgList& save_gif_external(const char *const filename, const unsigned int fps=25, + const CImgList& save_gif_external(const char *const filename, const float fps=25, const unsigned int nb_loops=0) { CImg command(1024), filename_tmp(256), filename_tmp2(256); CImgList filenames; @@ -49162,15 +53622,15 @@ namespace cimg_library_suffixed { } #if cimg_OS!=2 - cimg_snprintf(command,command._width,"%s -delay 1x%u -loop %u", - cimg::imagemagick_path(),fps,nb_loops); + cimg_snprintf(command,command._width,"%s -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)cimg::max(0.0f,cimg::round(100/fps)),nb_loops); CImg::string(command).move_to(filenames,0); cimg_snprintf(command,command._width,"\"%s\" >/dev/null 2>&1", CImg::string(filename)._system_strescape().data()); CImg::string(command).move_to(filenames); #else - cimg_snprintf(command,command._width,"\"%s -delay 1x%u -loop %u", - cimg::imagemagick_path(),fps,nb_loops); + cimg_snprintf(command,command._width,"\"%s -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)cimg::max(0.0f,cimg::round(100/fps)),nb_loops); CImg::string(command).move_to(filenames,0); cimg_snprintf(command,command._width,"\"%s\"\" >NUL 2>&1", CImg::string(filename)._system_strescape().data()); @@ -49505,7 +53965,8 @@ namespace cimg_library_suffixed { \param compression_type Compression mode used to write data. **/ const CImgList& save_tiff(const char *const filename, const unsigned int compression_type=0, - const float *const voxel_size=0, const char *const description=0) const { + const float *const voxel_size=0, const char *const description=0, + const bool is_bigtiff=true) const { if (!filename) throw CImgArgumentException(_cimglist_instance "save_tiff(): Specified filename is (null).", @@ -49513,21 +53974,18 @@ namespace cimg_library_suffixed { if (is_empty()) { cimg::fempty(0,filename); return *this; } #ifndef cimg_use_tiff - if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description); + if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,is_bigtiff); else cimglist_for(*this,l) { CImg nfilename(1024); cimg::number_filename(filename,l,6,nfilename); - _data[l].save_tiff(nfilename,compression_type,voxel_size,description); + _data[l].save_tiff(nfilename,compression_type,voxel_size,description,is_bigtiff); } #else - TIFF *tif = TIFFOpen(filename,"w"); + TIFF *tif = TIFFOpen(filename,is_bigtiff?"w8":"w4"); if (tif) { for (unsigned int dir = 0, l = 0; l<_width; ++l) { const CImg& img = (*this)[l]; - if (img) { - if (img._depth==1) img._save_tiff(tif,dir++,compression_type,voxel_size,description); - else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++,compression_type,voxel_size,description); - } + cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description); } TIFFClose(tif); } else @@ -49588,7 +54046,7 @@ namespace cimg_library_suffixed { CImg nfilename(1024); cimglist_for(*this,l) { cimg::number_filename(body,l,6,nfilename); - if (*ext) std::sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); + if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); _data[l].save_gzip_external(nfilename); } } @@ -49651,7 +54109,7 @@ namespace cimg_library_suffixed { #define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x)) const char - *const _codec = codec?codec:"mp4v", + *const _codec = codec && *codec?codec:"mp4v", codec0 = _cimg_docase(_codec[0]), codec1 = _codec[0]?_cimg_docase(_codec[1]):0, codec2 = _codec[1]?_cimg_docase(_codec[2]):0, @@ -49977,13 +54435,13 @@ namespace cimg_library_suffixed { \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width. **/ static const CImgList& font(const unsigned int font_height, const bool is_variable_width=true) { - if (!font_height) return CImgList::empty(); + if (!font_height) return CImgList::const_empty(); cimg::mutex(11); // Decompress nearest base font data if needed. - const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 }; - const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 }, - data_Ms[] = { 86,79,57,47 }; + static const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 }; + static const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 }, + data_Ms[] = { 86,79,57,47 }; const unsigned int data_ind = font_height<=13U?0U:font_height<=23U?1U:font_height<=53U?2U:3U; static CImg base_fonts[4]; CImg &base_font = base_fonts[data_ind]; @@ -50727,7 +55185,7 @@ namespace cimg { char *pd = _path; for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; } *pd = 0; - unsigned int lp = std::strlen(_path); + unsigned int lp = (unsigned int)std::strlen(_path); if (!_is_pattern && lp && _path[lp - 1]=='/') { _path[lp - 1] = 0; --lp; #if cimg_OS!=2 @@ -50747,7 +55205,7 @@ namespace cimg { } else { // No path to folder specified, assuming current folder. is_current = true; *_path = 0; } - lp = std::strlen(_path); + lp = (unsigned int)std::strlen(_path); } // Windows version. @@ -50757,21 +55215,19 @@ namespace cimg { std::memcpy(pattern,_path,lp); pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0; } - WIN32_FIND_DATAA file_data; const HANDLE dir = FindFirstFileA(pattern.data(),&file_data); - if (dir==INVALID_HANDLE_VALUE) return CImgList::empty(); + if (dir==INVALID_HANDLE_VALUE) return CImgList::const_empty(); do { const char *const filename = file_data.cFileName; if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { - const unsigned int lf = std::strlen(filename); + const unsigned int lf = (unsigned int)std::strlen(filename); const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0; if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) { if (include_path) { - CImg full_filename(lp + lf + 2); - std::memcpy(full_filename,_path,lp); - full_filename[lp] = '/'; - std::memcpy(full_filename._data + lp + 1,filename,lf + 1); + CImg full_filename((lp?lp+1:0) + lf + 1); + if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; } + std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1); full_filename.move_to(res); } else CImg(filename,lf + 1).move_to(res); } @@ -50782,12 +55238,12 @@ namespace cimg { // Unix version (posix). #else DIR *const dir = opendir(is_root?"/":is_current?".":_path.data()); - if (!dir) return CImgList::empty(); + if (!dir) return CImgList::const_empty(); struct dirent *ent; while ((ent=readdir(dir))!=0) { const char *const filename = ent->d_name; if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { - const unsigned int lf = std::strlen(filename); + const unsigned int lf = (unsigned int)std::strlen(filename); CImg full_filename(lp + lf + 2); if (!is_current) { @@ -50886,7 +55342,8 @@ namespace cimg { \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 unsigned int timeout, const bool try_fallback, + const char *const referer) { if (!url) throw CImgArgumentException("cimg::load_network(): Specified URL is (null)."); if (!filename_local) @@ -50921,6 +55378,7 @@ namespace cimg { curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L); if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout); if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L); + if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer); res = curl_easy_perform(curl); curl_easy_cleanup(curl); std::fseek(file,0,SEEK_END); // Check if file size is 0. @@ -50940,23 +55398,41 @@ namespace cimg { cimg::unused(try_fallback); // Try with 'curl' first. - if (timeout) - cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),timeout,filename_local,url); - else - cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"", - cimg::curl_path(),filename_local,url); + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"%s -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,timeout,filename_local,url); + else + cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),timeout,filename_local,url); + } else { + if (referer) + cimg_snprintf(command,command._width,"%s -e %s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,filename_local,url); + else + cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),filename_local,url); + } cimg::system(command); if (!(file = std::fopen(filename_local,"rb"))) { // Try with 'wget' otherwise. - if (timeout) - cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),timeout,filename_local,url); - else - cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", - cimg::wget_path(),filename_local,url); + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"%s --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,timeout,filename_local,url); + else + cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),timeout,filename_local,url); + } else { + if (referer) + cimg_snprintf(command,command._width,"%s --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,filename_local,url); + else + cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),filename_local,url); + } cimg::system(command); if (!(file = std::fopen(filename_local,"rb"))) @@ -51079,7 +55555,7 @@ namespace cimg { logo._data,is_centered); throw CImgIOException("cimg::dialog(): No display available."); #else - const unsigned char + static const unsigned char black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; // Create buttons and canvas graphics @@ -51281,6 +55757,9 @@ namespace cil = cimg_library_suffixed; #ifdef _cimg_redefine_PI #define PI 3.141592653589793238462643383 #endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif #endif // Local Variables: diff --git a/stim/parser/filename.h b/stim/parser/filename.h index bb61bbc..69e9ba0 100644 --- a/stim/parser/filename.h +++ b/stim/parser/filename.h @@ -215,7 +215,7 @@ public: hFind = FindFirstFileA((filepath.str().c_str()), &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { - printf ("Invalid file handle. Error is %u.\n", GetLastError()); + printf ("Invalid file handle. Error is %u.\n", GetLastError()); } else { std::string file_name = FindFileData.cFileName; //get the file name @@ -230,9 +230,8 @@ public: file_list.push_back(current_file); } FindClose(hFind); - + } return file_list; - } } #endif -- libgit2 0.21.4